///////////////////////////////////////////////////////////////
//                                                           //
//                   Alexander K. Haley's                    //
//                                                           //
//                      --re-written--                       //
//                                                           //
//               Golden Spike Pinball Machine                //
//                Master Controller Program                  //
//                                                           //
//                  Last Updated: 5/3/2021                   //
//                Re-write Began: 12/12/2020                 //
//                                                           //
//                       Build #  0025                       //
//                                                           //
//                Written with Visual Studio                 //
//                                                           //
///////////////////////////////////////////////////////////////
//NOTES:
//There are plenty of instances in this program where I do not use for() loops. This is to reduce the number of instructions the processor must execute. Faster runtime is worth more than program storage space.
//This buid has the game set to freeplay and coin counting on game start.

//TODO:
//There seems to be a bug that causes high scores to be copied where they shouldn't... look into that.

// Add something to differentiate between active tests
// Inform the operator on each startup (with the DMD) that there are more balls in the trough than the game needs.
// Notify player via DMD if game detects trough jam and needs to sort it out.
// Have game check if there is a jam in the trough during the self-test; deal with it if necessary.
// Implement a colorblind mode
// Make freeplay an option
// Attract mode lights, make em wait a bit after game ends before starting
// Add a preventative measure that handles if there are too many balls on the PF
// Video mode?
// Add in tilt stuff
// Implement the run once tests
// At game end, make all flashing lights either lit or unlit
// Easter eggs? (Ex. score on DMD less than certain amount when ball drains, say "Well that sucks" or "Gee, that sucks" or "Ooof, off to great start", "Chuck Norris already built both railroads with his bare hands"
// or "My grandma could do better" or if Central Pacific "Don't worry, the Union Pacific still hasn't left Omaha yet..."

//CHANGES:
//Operator-adjustable settings

//////////////////////////////////////////////////////
//                                                  //
//		 Operator-Adjustible General Settings       //
//                                                  //
//////////////////////////////////////////////////////
//When it doesn't make sense for a setting to have anything other than a "Yes" or "No", 1 is Yes and 0 is No.
//Test Mode Stuff
byte ForceTestMode = 0; //Whether or not to boot the game in test mode, regardless of whether or not the test switch is flipped.
byte VerboseTestMode = 0; //Whether or not to print status messages/test mode stuff to the Serial Monitor in addition to what is shown on the DMD. 
byte VerboseSelfTest = 0; //If 0, only print DMD messages where the self-test fails. If 1, show all messages.

//Credits
byte PricePerGame = 0; //Number of quarters each game costs
byte CoinCountingEnabled = 1; //whether or not to have the game toggle the coin counter when a coin is dropped.
byte TriggerPerCoin = 0; //1 triggers per coin, 0 triggers per game.

//Ball stuff
byte NumBallsInstalled = 3; //How many balls are in the machine. Can be any # from 3 to 8.
int NumBallsPerGame = 3; //3 recommended, could do up to 255 but why?

//High Scores
byte SaveHighScores = 1; //0 is off, 1 is on

//Playfield Lighting
const uint8_t PlayfieldLightBrightness = 192; //The brightness of everything on the PF. 0 to 255.

//byte ColorblindModeActive = 0; //The game uses 3 colors: white, red, and yellow. 0 is off, 1 replaces red with blue, 2 replaces yellow with green. I have some colorblind friends/family.

//////////////////////////////////////////////////////
//                                                  //
//		   Operator-Adjustible Game Settings        //
//                                                  //
//////////////////////////////////////////////////////

//Autofire
unsigned long ImpatientAutofireInterval = 15000; //The length of time (in milliseconds) that the autofire gives the player to use the plunger before it decides to take matters into its own hands. Useful for auto-ending a game if a player walks away and forgets.

//Shoot Again
unsigned long ShootAgainGracePeriod = 15000; //The length of time (in milliseconds) the game will save your ball if it drains.
unsigned long INDIANSMultiballGracePeriod = 20000;
unsigned long TrackFrenzyMultiballGracePeriod = 20000;
unsigned long RailroadCompletedMultiballGracePeriod = 20000;

//Drop target reset
byte ResetDropTargetBetweenBalls = 0; 

//Extra Ball
unsigned long ExtraBallAwardedAtPointScore = 30000; //How many points before the game makes the supply tower award an extra ball if hit.

//Rewards
byte TypeOfMatchReward = 0; //0 is an extra ball, 1 is an extra game.
byte FreeGameOnHighScore = 0;

//High Score
unsigned long TimeGivenToEnterInitials = 120000; //In milliseconds. 

//tilt sensitivity

//////////////////////////////////////////////
//                                          //
//	       	 Included Libraries             //
//                                          //
//////////////////////////////////////////////
#include <FastLED.h>
#include <FastLED_RGBW.h>

#include <SPI.h>
#include <SD.h>

//////////////////////////////////////////////
//                                          //
//		       Pin Declarations             //
//                                          //
//////////////////////////////////////////////
//Drive lines are Rows
byte DriveZero = 30;
byte DriveOne = 31;
byte DriveTwo = 32;
byte DriveThree = 33;
byte DriveFour = 34;
byte DriveFive = 35;
byte DriveSix = 36;
byte DriveSeven = 37;
byte DriveEight = 38;
byte DriveNine = 39;
byte DriveTen = 40;
byte DriveEleven = 41;
byte DriveTwelve = 42;
byte DriveThirteen = 43;
byte DriveFourteen = 44;
byte DriveFifteen = 45;

//Sense lines are columns
byte SenseZero = 46;
byte SenseOne = 47;
byte SenseTwo = 48;
byte SenseThree = 49;
byte SenseFour = 50;
byte SenseFive = 51;
byte SenseSix = 52;
byte SenseSeven = 53;

//Coil control lines
byte CoilAddressZero = 2;
byte CoilAddressOne = 3;
byte CoilAddressTwo = 4;
byte CoilAddressThree = 5;
byte CoilAddressFour = 6;

byte CoilData = 7;
byte CoilLatchEnable = 8; //(AKA WriteToCoil on Schematic) ACTIVE LOW

//LED Controller Pins
#define NeoPixelZero 22
#define NeoPixelOne 23
#define NeoPixelTwo 24
#define NeoPixelThree 25
#define NeoPixelFour 26
#define NeoPixelFive 27
#define NeoPixelSix 28
#define NeoPixelSeven 29

//////////////////////////////////////////////
//                                          //
//		      Switch Matrix Stuff           //
//                                          //
//////////////////////////////////////////////
unsigned long LastSwitchMatrixReadTime = 0;

byte LastSwitchState[16][8];
byte CurrentSwitchState[16][8];

unsigned long LastHitTime[16][8];
unsigned long TargetDebounceInterval[16][8] = {
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{25,  100, 100, 100, 100, 100, 100, 25},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 150, 100, 200, 100, 150, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100}
};
byte HitSwitches[16][8];

//////////////////////////////////////////////
//                                          //
//		        NeoPixel Stuff              //
//                                          //
//////////////////////////////////////////////
#define NumLightsLowerPf 24 //Lower PF number of lights in string
#define NumLightsUpperPf 26 //Upper PF number of lights in string

CRGBW lowerleds[NumLightsLowerPf]; //The led arrays that the game program will be manipulating.
CRGBW upperleds[NumLightsUpperPf];

CRGB* ledsRGBl = (CRGB*)&lowerleds[0]; //I don't really understand this...
CRGB* ledsRGBu = (CRGB*)&upperleds[0]; //I just know it works. �\_(^u^)_/�

byte LowerLEDState[24]; //0 means off, 1 means on, 2 means flashing at rate determined by other array.
byte LowerLEDColor[24]; //0 means off, 1 means red, 2 means orange, 3 means yellow... ROYGBIVW to keep things easy. 
unsigned long LowerLEDSpeed[24]; //The length of time the LED spends on or off while flashing.
unsigned long LowerLEDLastLit[24]; //Last time state changed; used for flashing function.

byte UpperLEDState[26]; //0 means off, 1 means on, 2 means flashing at rate determined by other array.
byte UpperLEDColor[26]; //0 means off, 1 means red, 2 means orange, 3 means yellow... ROYGBIVW to keep things easy. 
unsigned long UpperLEDSpeed[26]; //The length of time the LED spends on or off while flashing.
unsigned long UpperLEDLastLit[26]; //Last time state changed; used for flashing function.

/* Playfield lights by string
String 1 - Lower PF
1. E //backwards from how it is spelled
2. N
3. I
4. G
5. N
6. E //beginning of word
7. "S"
8. "T"
9. "E"
10. "A"
11. "M"
12. UPRR 1
13. UPRR 2
14. CPRR 1
15. CPRR 2
16. All track laid
17. All trestles built
18. All tunnels blasted
19. Tunnel blasted
20. Trestle built
21. Track frenzy
22. Acquired locomotive
23. Defended against indians
24. Shoot again light

String 2 - Upper PF
1. T
2. C
3. R
4. R
5. Right arrow top
6. Right arrow middle
7. Right arrow bottom
8. india"N"s
9. indian"S"
10. Right nitroglycerine
11. Tunnel VuK stealth bomber
12. Left nitroglycerine
13. indi"A"ns
14. ind"I"ans
15. Supply tower VuK stealth bomber
16. in"D"ians
17. i"N" dians
18. Indian attack VuK stealth bomber
19. "I"ndians
20. Left arrow bottom
21. Left arrow middle
22. Left arrow top
23. R
24. A
25. I
26. L
*/

//////////////////////////////////////////////
//                                          //
//	       	  SD Card Variables             //
//                                          //
//////////////////////////////////////////////

File RECORDSFile;

byte SDChipSelect = 12;
byte SDFailLED = 11;
byte SDWorking = 0;

byte HighScoreSendState = 0;
byte HighScoresSentYet = 0;
unsigned long HighScoreSendMillis = 0;

//////////////////////////////////////////////
//                                          //
//		      General Variables             //
//                                          //
//////////////////////////////////////////////
unsigned long CurrentProgramMillis = 0;

unsigned long KickerClearMillis = 0;
unsigned long NumKickersCleared = 0;

unsigned long ImpatientAutofireMillis = 0;

unsigned long BallCounterDisableMillis = 0;
unsigned long ShootAgainMillis = 0;

unsigned long STEAMCompletedMillis = 0;
unsigned long TCRRCompletedMillis = 0;
unsigned long ENGINECompletedMillis = 0;
unsigned long INDIANSCompletedMillis = 0;

byte CurrentBallNum = 0;
byte ExtraBallsEarned = 0;
byte NumBallsInPlay = 0; //The difference between NumBallsInstalled and the NumBallsInTrough.
byte NumBallsShouldBeInPlay = 0; //How many balls should be in play according to game state. Regular game - 1 ball, multiball - 2+ balls, etc.
byte NumBallsInTrough = 0; //Exactly as name implies. The trough holds up to 8 balls.
byte BallCounterDisabledByExecutiveOrder = 0; //Disables counting the number of balls in play during instances where checking the number of balls would make the program move to the next state faster than intended. Ex, during the end-of-ball bonus.
byte BallsInTroughLogicDisabledByExecutiveOrder = 0; //Disables a lot of ball control stuff when checking the number of balls would make the program move to the next state faster than intended. Ex, while waiting for DT to reset before next ball.
byte TroughJammed = 0;

byte NumCoinsInserted = 0;

byte GameStartState = 0; //Used for timing
unsigned long GameStartMillis = 0;

byte BallEndState = 0;
unsigned long BallEndMillis = 0;

byte DropTargetBeingDebounced = 0;
byte DropTargetNeedsToBeReset = 0;
unsigned long DropTargetResetMillis = 0;
byte DiverterNeedsToClose = 0;

unsigned long DiagnosticMillis = 0;
byte DiagnosticNumber = 0;
byte FlippersEnabled = 0;
//////////////////////////////////////////////
//                                          //
//		  Gameplay-Related Variables        //
//                                          //
//////////////////////////////////////////////
byte TestMode = 0;
byte GameActive = 0;

byte STEAMTriggered[6];
byte TCRRTriggered[5];
byte ENGINETriggered[6];
byte RAILTriggered[4];

byte STEAMCompleted = 0;
byte TCRRCompleted = 0;
byte ENGINECompleted = 0;
byte RAILCompleted = 0;

byte MilesOfTrackPerHit = 2;

//Self-test stuff
byte SelfTestPassed = 0;
byte SelfTestState = 0;
unsigned long SelfTestMessageMillis = 0;

//Extra ball
unsigned long LastExtraBallScore = 0;
byte ExtraBallMessageState = 0;
unsigned long ExtraBallMessageMillis = 0;

//Track status report
byte TrackProgressMessageState = 0;
unsigned long TrackProgressMessageMillis = 0;

//Indians Attack 2-Ball Multiball
byte INDIANSModeActive = 0;
byte INDIANSTriggered[7];
byte INDIANSCompleted = 0;
byte INDIANSGoalCompleted = 0;
byte INDIANSModeStage = 0;
unsigned long INDIANSModeMillis = 0;

byte INDIANSModeMessageState = 0;
unsigned long INDIANSModeMessageMillis = 0;

byte MutuallyExclusiveModeActive = 0; //Used as a convenient way to check if another mode can be started.
byte MultiballModeActive = 0;

//Supply Tower Random Reward
byte RandomlyGeneratedNumber = 0;
byte SupplyTowerRewardThisLoop = 0;
byte SupplyTowerStage = 0;
unsigned long SupplyTowerMillis = 0;

unsigned long SupplyTowerMilesOfTrackReward = 30; //Up to 255
unsigned long SupplyTowerPointBonusReward = 1000; //Up to 65535

//Track Frenzy 2-Ball Multiball
byte TrackFrenzyModeActive = 0;
byte TrackFrenzyModeStage = 0;
byte TrackFrenzyHappenedYet = 0;
byte TrackFrenzyMilesGoal = 100;

unsigned long SupplyTowerTrackAwardDuringFrenzy = 15; //miles of track

unsigned long MilesOfTrackBuiltDuringFrenzy = 0;

unsigned long TrackFrenzyModeMillis = 0;

unsigned long TrackFrenzyLightMillis = 0;
unsigned long TrackFrenzyLightState = 0;

//Railroad Completed 3-Ball Multiball
byte RailroadCompletedMultiballModeActive = 0;
byte RailroadCompletedMultiballStage = 0;

unsigned long RailroadCompletedMultiballMillis = 0;

//VUKs
byte VUKTriggered[4]; // INDIANS, Supply Tower, Trestle, Tunnel    in that order
unsigned long VUKTriggeredMillis[4]; // INDIANS, Supply Tower, Trestle, Tunnel    in that order

//Tunnel blasting
byte PartialTunnelsCompleted = 0; //Pieces of a whole tunnel
byte NitroglycerineHit[2];
byte TunnelMessageStatus = 0;

//Trestle building
byte PartialTrestlesCompleted = 0; //Pieces of a whole trestle
byte DiverterOpen = 0;
byte DiverterOpenedYet = 0;
unsigned long ImpatientDiverterMillis = 0;
unsigned long TrestleMessageMillis = 0;
byte TrestleMessageState = 0;

byte TrestleLightState = 0;
unsigned long TrestleLightMillis = 0;

//End of ball bonus
byte EndOfBallBonusStage = 0;
byte StageHappenedYet = 0;
unsigned long EndOfBallBonusMillis = 0;

//Match
byte MatchHappenedYetThisGame = 0;
byte MatchState = 0;
byte RandomMatchNumber = 0;
unsigned long MatchMillis = 0;

//Shoot Again
byte BallSavedState = 0;
unsigned long BallSavedMillis = 0;

byte ShootAgainActive = 0; //Just used for the shoot again light; may intentionally vary from whether or not the shoot again/ball saver is active. 
byte BallJustSaved = 0;

//Railroad stuff
byte RailroadSelectModeActive = 0; //Whether or not the player is choosing their railroad
unsigned long ImpatientRailroadSelectMillis = 0;

byte ChosenRailroad = 1; //1 if Central Pacific, 2 if Union Pacific. Is determined by player. I have CPRR bias :)
byte CentralPacificCompleted = 0;
byte UnionPacificCompleted = 0;
byte RailroadCompleted = 0;

//High score mode
byte HighScoreArray[9][15]; //The array of high scores
unsigned long AssembledHighScores[9];

byte HighScoreRank = 0;

byte CurrentlySelectedHighScoreLetter = 77;
byte CharacterSelectionPosition = 0;
byte HighScoreMessageState = 0;
unsigned long HighScoreMessageMillis = 0;

unsigned long HoldingFlipperButtonMillis = 0;
unsigned long LastAutoScrollMillis = 0;

//Goal variables
unsigned long MilesOfTrackLaid = 0;
unsigned long TrackFrenziesCompleted = 0;
unsigned long NumberOfEnginesAcquired = 0;
unsigned long NumberOfTunnelsBlasted = 0;
unsigned long NumberOfTrestlesBuilt = 0;
unsigned long NumberOfIndianSquabbles = 0;

//Differences between goals for RR companies and the real numbers are purely for balancing. 
//Central Pacific Goals
unsigned long CPRRMilesOfTrack = 690; //CPRR built 690 miles of track
unsigned long CPRRTunnelsBlasted = 3; //CPRR blasted 15 tunnels
unsigned long CPRRTrestlesBuilt = 2; //Not sure how many major trestles the CPRR built, 3 seems good.
unsigned long CPRRTrackFrenzies = 1;
unsigned long CPRRLocomotivesAcquired = 1; //An 1868 locomotive roster shows that at the time of the RR's completion, the CPRR had a total of 176 locomotives.
                                           //Although, this is based on date of construction, not date of delivery, so some locomotives could be in-situ.
                                           //Another source puts it at 162 locomotives.
unsigned long CPRRIndianSquabbles = 1;

//Union Pacific Goals
unsigned long UPRRMilesOfTrack = 1086; //UPRR built 1086 miles of track
unsigned long UPRRTunnelsBlasted = 2; //UPRR blasted 4 tunnels
unsigned long UPRRTrestlesBuilt = 3; //UPRR built 8 major trestles
unsigned long UPRRTrackFrenzies = 1;
unsigned long UPRRLocomotivesAcquired = 1; //Could not find roster for UPRR; exact number unknown.
unsigned long UPRRIndianSquabbles = 1;

//////////////////////////////////////////////
//                                          //
//		       Score Variables              //
//                                          //
//////////////////////////////////////////////
unsigned long PlayerOneScore = 0;

//Target score variables
unsigned long PopBumperScore = 10;
unsigned long SlingshotScore = 45;
unsigned long SpinnerScore = 15;
unsigned long TCRRLaneScore = 20;
unsigned long ReturnLaneScore = 50;
unsigned long OutlaneScore = 100;
unsigned long ENGINELetterScore = 60;
unsigned long INDIANSKickerScore = 300;
unsigned long INDIANSNormalLetterScore = 20;
unsigned long INDIANSModeLetterScore = 100;
unsigned long RAILNormalLetterScore = 50;
unsigned long PartialTunnelScore = 600;
unsigned long TunnelBlastedScore = 1200;
unsigned long PartialTrestleScore = 200;
unsigned long TrestleBuiltScore = 400;
unsigned long SupplyTowerHitScore = 250;

//End-of-ball bonus variables (in order of the sequence)
unsigned long BonusPerMileOfTrack = 10;
unsigned long BonusPerTrackFrenzy = 250;
unsigned long BonusPerTrestleBuilt = 100;
unsigned long BonusPerTunnelBlasted = 100;
unsigned long BonusPerLocomotiveAcquired = 150;
unsigned long BonusPerIndianSquabble = 250;

//Sequence & mode score variables
unsigned long TCRRCompletedScore = 150;
unsigned long STEAMCompletedScore = 500;
unsigned long ENGINECompletedScore = 500;
unsigned long INDIANSModeCompletedScore = 500;
unsigned long RailroadCompletionScoreBonus = 10000;

//////////////////////////////////////////////
//                                          //
//		 Coil Control & Firing Times        //
//                                          //
//////////////////////////////////////////////
//unsigned long CoilFireLength = 15; //16 and 17 generally seemed to work with 1 ball for autofire, 2 balls fires one into the trough. May need to look into that.

byte CurrentCoil = 0;
unsigned long CoilTestFireMillis = 0;

byte CoilFireFlags[4][8];
byte CoilState[4][8];

unsigned long CoilFireMillis[4][8];

//15 milliseconds seems to work for most coils but look into that.
unsigned long CoilFireLength[4][8] = {
	{6, 6, 16, 14, 4, 15, 0, 350},
	{5, 0, 0, 0, 0, 0, 0, 0},
	{6, 6, 17, 14, 4, 15, 0, 15},
	{0, 0, 0, 0, 0, 0, 0, 0}
};

//Minimum amount of time required between firing any given coil. Great failsafe if a program bug keeps writing a coil HIGH.
unsigned long CoilFireCooldown[4][8] = {
	{100, 100, 100, 100, 125, 100, 100, 0},
	{100, 100, 100, 100, 100, 100, 100, 100},
	{100, 100, 100, 100, 125, 100, 100, 100},
	{100, 100, 100, 100, 100, 100, 100, 100}
};

/* COILS
Bank 0
0 - Pop 1
1 - Pop 2
2 - RAIL VuK
3 - Supply Tower VuK
4 - Left Slingshot
5 - Trough
6 - Diverter
7 - Start Light    (Yes, the start light is to be controlled like a coil.)

Bank 1
8 - Coin Counter

Bank 3
16 - Pop 3
17 - Pop 4
18 - Mtn VuK
19 - Tunnel VuK
20 - Right Slingshot
21 - Autofire
22 - Flipper Relay
23 - Drop Target Reset
*/

//////////////////////////////////////////////
//                                          //
//		   Communication Variables          //
//                                          //
//////////////////////////////////////////////
byte NewDataPresent = 0; //Set to 1 so that the Master Arduino will state that it is in the attract mode to the DMD.

//Variables used to break down other variables for transmission
unsigned long ScoreBreakdownVariable = 0; //Used for converting the binary PlayerOneScore into base-10 digits
byte MathNumberOfCredits = 0;
byte TransmissionNumberOfCredits = 0;
unsigned long MathVariable = 0;
unsigned long MathVariableTwo = 0;

byte DataToSend[21];
byte LastDataThatWasSent[21];
//Each byte of data, in order:
//0. Unique number
//1. Game Active
//2. Billions (Score)
//3. Hundred Millions
//4. Ten Millions
//5. Millions
//6. Hundred Thousands
//7. Ten Thousands
//8. Thousands
//9. Hundreds
//10. Tens
//11. Ones
//12. Credit Tens
//13. Credit Ones
//14. Ball Number
//15. Current Mode
//16. Stage of mode
//17. Mode Variable One
//18. Mode Variable Two
//19. Mode Variable Three
//20. Mode Variable Four

/////////////////////////////////////
//                                 //
//        ASCII definitions        //
//                                 //
/////////////////////////////////////

//Not hooking this up to a printer, so I don't need chars 0-31
#define ASCII_SPACE             32   //
#define ASCII_EXCLAMATION_MARK  33   // !
#define ASCII_DOUBLE_QUOTE      34   // "
#define ASCII_HASHTAG           35   // #
#define ASCII_DOLLAR_SIGN       36   // $
#define ASCII_PER_CENT_SIGN     37   // %
#define ASCII_AMPERSAND         38   // &
#define ASCII_SINGLE_QUOTE      39   // '
#define ASCII_OPEN_PARENTHESES  40   // (
#define ASCII_CLOSE_PARENTHESES 41   // )
#define ASCII_ASTERISK          42   // *
#define ASCII_PLUS              43   // +
#define ASCII_COMMA             44   // ,
#define ASCII_HYPHEN            45   // -
#define ASCII_PERIOD            46   // .
#define ASCII_SLASH             47   // /
#define ASCII_ZERO              48   // 0
#define ASCII_ONE               49   // 1
#define ASCII_TWO               50   // 2
#define ASCII_THREE             51   // 3
#define ASCII_FOUR              52   // 4
#define ASCII_FIVE              53   // 5
#define ASCII_SIX               54   // 6
#define ASCII_SEVEN             55   // 7
#define ASCII_EIGHT             56   // 8
#define ASCII_NINE              57   // 9
#define ASCII_COLON             58   // :
#define ASCII_SEMICOLON         59   // ;
#define ASCII_LESS_THAN         60   // <
#define ASCII_EQUAL             61   // =
#define ASCII_GREATER_THAN      62   // >
#define ASCII_QUESTION_MARK     63   // ?
#define ASCII_AT_SYMBOL         64   // @
#define ASCII_UPPERCASE_A       65   // A
#define ASCII_UPPERCASE_B       66   // B
#define ASCII_UPPERCASE_C       67   // C 
#define ASCII_UPPERCASE_D       68   // D
#define ASCII_UPPERCASE_E       69   // E
#define ASCII_UPPERCASE_F       70   // F
#define ASCII_UPPERCASE_G       71   // G
#define ASCII_UPPERCASE_H       72   // H
#define ASCII_UPPERCASE_I       73   // I
#define ASCII_UPPERCASE_J       74   // J
#define ASCII_UPPERCASE_K       75   // K
#define ASCII_UPPERCASE_L       76   // L
#define ASCII_UPPERCASE_M       77   // M
#define ASCII_UPPERCASE_N       78   // N
#define ASCII_UPPERCASE_O       79   // O
#define ASCII_UPPERCASE_P       80   // P
#define ASCII_UPPERCASE_Q       81   // Q
#define ASCII_UPPERCASE_R       82   // R
#define ASCII_UPPERCASE_S       83   // S
#define ASCII_UPPERCASE_T       84   // T
#define ASCII_UPPERCASE_U       85   // U
#define ASCII_UPPERCASE_V       86   // V
#define ASCII_UPPERCASE_W       87   // W
#define ASCII_UPPERCASE_X       88   // X
#define ASCII_UPPERCASE_Y       89   // Y
#define ASCII_UPPERCASE_Z       90   // Z
#define ASCII_OPENING_BRACKET   91   // [
#define ASCII_BACKSLASH         92   // \\        //Apparently the backslash comments out the line after, a space after it fixes that though
#define ASCII_CLOSING_BRACKET   93   // ]
#define ASCII_CARET             94   // ^
#define ASCII_UNDERSCORE        95   // _
#define ASCII_GRAVE_ACCENT      96   // `
#define ASCII_LOWERCASE_A       97   // a
#define ASCII_LOWERCASE_B       98   // b
#define ASCII_LOWERCASE_C       99   // c
#define ASCII_LOWERCASE_D       100  // d
#define ASCII_LOWERCASE_E       101  // e
#define ASCII_LOWERCASE_F       102  // f
#define ASCII_LOWERCASE_G       103  // g
#define ASCII_LOWERCASE_H       104  // h
#define ASCII_LOWERCASE_I       105  // i
#define ASCII_LOWERCASE_J       106  // j
#define ASCII_LOWERCASE_K       107  // k
#define ASCII_LOWERCASE_L       108  // l
#define ASCII_LOWERCASE_M       109  // m
#define ASCII_LOWERCASE_N       110  // n
#define ASCII_LOWERCASE_O       111  // o
#define ASCII_LOWERCASE_P       112  // p
#define ASCII_LOWERCASE_Q       113  // q
#define ASCII_LOWERCASE_R       114  // r
#define ASCII_LOWERCASE_S       115  // s
#define ASCII_LOWERCASE_T       116  // t
#define ASCII_LOWERCASE_U       117  // u
#define ASCII_LOWERCASE_V       118  // v
#define ASCII_LOWERCASE_W       119  // w
#define ASCII_LOWERCASE_X       120  // x
#define ASCII_LOWERCASE_Y       121  // y
#define ASCII_LOWERCASE_Z       122  // z
#define ASCII_OPENING_BRACE     123  // {
#define ASCII_VERTICAL_BAR      124  // |
#define ASCII_CLOSING_BRACE     125  // }
#define ASCII_EQUIVALENCY_SIGN_TILDE 126  // ~
#define ASCII_DELETE            127  // <-

#define ASCII_COPYRIGHT         184  //(c)



// The setup() function runs once each time the micro-controller starts
void setup()
{
	//Coil Stuff
	pinMode(DriveZero, OUTPUT);
	pinMode(DriveOne, OUTPUT);
	pinMode(DriveTwo, OUTPUT);
	pinMode(DriveThree, OUTPUT);
	pinMode(DriveFour, OUTPUT);
	pinMode(DriveFive, OUTPUT);
	pinMode(DriveSix, OUTPUT);
	pinMode(DriveSeven, OUTPUT);
	pinMode(DriveEight, OUTPUT);
	pinMode(DriveNine, OUTPUT);
	pinMode(DriveTen, OUTPUT);
	pinMode(DriveEleven, OUTPUT);
	pinMode(DriveTwelve, OUTPUT);
	pinMode(DriveThirteen, OUTPUT);
	pinMode(DriveFourteen, OUTPUT);
	pinMode(DriveFifteen, OUTPUT);

	pinMode(SenseZero, INPUT);
	pinMode(SenseOne, INPUT);
	pinMode(SenseTwo, INPUT);
	pinMode(SenseThree, INPUT);
	pinMode(SenseFour, INPUT);
	pinMode(SenseFive, INPUT);
	pinMode(SenseSix, INPUT);
	pinMode(SenseSeven, INPUT);

	pinMode(CoilAddressZero, OUTPUT);
	pinMode(CoilAddressOne, OUTPUT);
	pinMode(CoilAddressTwo, OUTPUT);
	pinMode(CoilAddressThree, OUTPUT);
	pinMode(CoilAddressFour, OUTPUT);

	pinMode(CoilData, OUTPUT);
	pinMode(CoilLatchEnable, OUTPUT);

	digitalWrite(CoilLatchEnable, HIGH);

	pinMode(SDFailLED, OUTPUT);
	pinMode(SDChipSelect, OUTPUT);

	//Turn off all the coils, since who knows what those latches power up with...
	for (byte x = 0; x < 31; x++)
	{
		WriteToCoil(x, 0);
	}

	//LED Stuff
	FastLED.addLeds<WS2812B, NeoPixelTwo, RGB>(ledsRGBu, getRGBWsize(NumLightsUpperPf));
	FastLED.addLeds<WS2812B, NeoPixelOne, RGB>(ledsRGBl, getRGBWsize(NumLightsLowerPf));
	FastLED.setBrightness(PlayfieldLightBrightness);
	FastLED.show();

	Serial.begin(115200);
	Serial3.begin(115200); //Initializes the channel that the main controller will use to communicate with the DMD.

	if (ForceTestMode == 1)
	{
		Serial.println(F("Golden Spike is booting into a Forced Test Mode."));
	}
	else if (TestMode == 1)
	{
		Serial.println(F("Golden Spike is booting into a the regular Test Mode."));
	}
	else
	{
		Serial.println(F("Golden Spike is booting normally."));
	}

	if (VerboseTestMode == 1)
	{
		Serial.println(F("Golden Spike has Verbose Test Mode enabled."));
		Serial.println(F("The game program will run slower from printing items to the serial monitor."));
	}
	else
	{
		Serial.println(F("Golden Spike does not have Verbose Test Mode enabled; it will only print normal items to the serial monitor."));
		Serial.println(F("The game program will run at the normal speed."));
	}

	randomSeed(analogRead(0)); //Set the random number generator to use a base value from a floating pin for real randomness.

	Serial.println(F(" "));
	Serial.println(F(" "));

	delay(1000); //Make sure the DMD is ready

	//Initialize the SD card
	if (SD.begin(SDChipSelect))
	{
		digitalWrite(SDFailLED, LOW);
		Serial.println(F("SD Card initialized"));

		SDWorking = 1;

		//Open the SD File
		if (SD.exists(F("RECORDS.txt")) == 1) //I wanted to say "HighScores", but the SD library is only capable of 8.3 names :/
		{
			RECORDSFile = SD.open(F("RECORDS.txt"));
			if (RECORDSFile.available())
			{
				for (byte x = 0; x < 9; x++)
				{
					for (byte y = 0; y < 15; y++)
					{
						if (y > 4) //score part
						{
							HighScoreArray[x][y] = RECORDSFile.read() - 48; //Converts ASCII 0-9 (48-57) to binary 0-9
						}
						else
						{
							HighScoreArray[x][y] = RECORDSFile.read(); //Read contents into the High Score Array
						}

						if (y == 14)
						{
							RECORDSFile.read(); //Get past the ASCII carriage return character
							RECORDSFile.read(); //Gets past the ASCII newline character
						}
					}
				}
			}
			RECORDSFile.close();
		}
		else //Create a new file and write to it
		{
			RECORDSFile = SD.open(F("RECORDS.txt"), FILE_WRITE);
			for (byte x = 0; x < 9; x++) //High Score #1 is top line, #9 is bottom
			{
				RECORDSFile.print(F("_____")); //Read contents into High Score Array
				RECORDSFile.print(F("0000000000"));
				RECORDSFile.println(F("")); //prints a carriage return and a newline
			}
			RECORDSFile.close();
		}
	}
	else
	{
		digitalWrite(SDFailLED, HIGH);
		Serial.println(F("SD Failed to Initialize"));

		SDWorking = 0;
	}
}

// Add the main program code into the continuous loop() function
void loop()
{
	CurrentProgramMillis = millis();
	ReadSwitches();
	RunOnceTests();
	GameLogic();
	FireUnfireCoils();
	LightControl();
	SendAndReceiveData();

	/*if (CurrentProgramMillis > DiagnosticMillis + 1000)
	{
		DiagnosticNumber++;
		if (DiagnosticNumber == 255)
		{
			DiagnosticNumber = 0;
		}
		DiagnosticMillis = CurrentProgramMillis;
		if (CurrentSwitchState[6][0] == 1)
		{
			Serial.println(F("T8"));
		}
		if (CurrentSwitchState[6][1] == 1)
		{
			Serial.println(F("T7"));
		}
		if (CurrentSwitchState[6][2] == 1)
		{
			Serial.println(F("T6"));
		}
		if (CurrentSwitchState[6][3] == 1)
		{
			Serial.println(F("T5"));
		}
		if (CurrentSwitchState[6][4] == 1)
		{
			Serial.println(F("T4"));
		}
		if (CurrentSwitchState[6][5] == 1)
		{
			Serial.println(F("T3"));
		}
		if (CurrentSwitchState[6][6] == 1)
		{
			Serial.println(F("T2"));
		}
		if (CurrentSwitchState[7][2] == 1)
		{
			Serial.println(F("T1"));
		}
		Serial.println(NumBallsInTrough);
		if (CurrentProgramMillis < BallCounterDisableMillis + 1000)
		{
			Serial.println(F("CTR. Disabled"));
		}
		if (BallsInTroughLogicDisabledByExecutiveOrder == 1)
		{
			Serial.println(F("Exec. Order"));
		}
		if (FlippersEnabled == 1)
		{
			Serial.println(F("Flip ON"));
		}
		else
		{
			Serial.println(F("Flip OFF"));
		}
		Serial.println(EndOfBallBonusStage);
		if (TroughJammed == 1)
		{
			Serial.println(F("Trough Jam"));
		}
		Serial.print(F("CTR:"));
		Serial.println(DiagnosticNumber);
		Serial.println();
		Serial.println();
	}*/
}

//////////////////////////////////////////////
//                                          //
//		          FUNCTIONS                 //
//                                          //
//////////////////////////////////////////////

//////////////////////////////////////////////
//                                          //
//		          Game logic                //
//                                          //
//////////////////////////////////////////////

void GameLogic()
{
	//This function handles almost EVERYTHING the game does.

	if (HitSwitches[8][4] == 1) //Checks if the operator has put the game into test mode.
	{
		TestMode = 1;
		Serial.println(F("Golden Spike is now in Test Mode."));
	}
	else if (LastSwitchState[8][4] == 1 && CurrentSwitchState[8][4] == 0)
	{
		TestMode = 0;
		Serial.println(F("Golden Spike has left Test Mode."));
	}

	if (HitSwitches[8][3] == 1 && PricePerGame != 0) //Has a coin been inserted?
	{
		NumCoinsInserted++;

		if (CoinCountingEnabled == 1 && TriggerPerCoin == 1)
		{
			CoilFireFlags[1][0] = 1; //Triggers coin counter
		}
	}

	if (NumCoinsInserted >= PricePerGame && GameActive == 0) //Is it possible for a game to be started?
	{
		if (CurrentProgramMillis > CoilFireMillis[0][7] + 700) //This will have the effect of making the start button's light flash.
		{
			CoilFireFlags[0][7] = 1;
		}
	}

	if (HitSwitches[8][2] == 1 && NumCoinsInserted >= PricePerGame && GameActive == 0 && GameStartState == 0) //Should a game be started?
	{
		NumCoinsInserted -= PricePerGame;

		if (CoinCountingEnabled == 1 && TriggerPerCoin == 0)
		{
			CoilFireFlags[1][0] = 1;
		}

		//Turn off all of the PF lights
		for (int x = 0; x < 24; x++)
		{
			LowerLEDState[x] = 0;
		}
		for (int x = 0; x < 26; x++)
		{
			UpperLEDState[x] = 0;
		}

		//Clear the TCRR, STEAM, ENGINE, INDIAN, and RAIL triggered arrays.
		for (int x = 0; x < 5; x++)
		{
			TCRRTriggered[x] = 0;
		}
		for (int x = 0; x < 6; x++)
		{
			STEAMTriggered[x] = 0;
		}
		for (int x = 0; x < 6; x++)
		{
			ENGINETriggered[x] = 0;
		}
		for (int x = 0; x < 7; x++)
		{
			INDIANSTriggered[x] = 0;
		}
		for (int x = 0; x < 4; x++)
		{
			RAILTriggered[x] = 0;
		}

		BallsInTroughLogicDisabledByExecutiveOrder = 0;

		MilesOfTrackLaid = 0;
		NumberOfTunnelsBlasted = 0;
		NumberOfTrestlesBuilt = 0;
		NumberOfIndianSquabbles = 0;
		NumberOfEnginesAcquired = 0;
		TrackFrenziesCompleted = 0;
		TrackFrenzyHappenedYet = 0;

		PlayerOneScore = 0;

		PartialTrestlesCompleted = 0;
		PartialTunnelsCompleted = 0;
		NitroglycerineHit[0] = 0;
		NitroglycerineHit[1] = 0;

		CentralPacificCompleted = 0;
		UnionPacificCompleted = 0;
		RailroadCompleted = 0;

		DiverterOpen = 0;

		GameStartState = 1;

		MatchHappenedYetThisGame = 0;
		MatchState = 0;

		RailroadSelectModeActive = 1;
		ImpatientRailroadSelectMillis = CurrentProgramMillis;

		HitSwitches[8][2] = 0; //To make the choose RR mode work
	}

	if (GameStartState == 1 && RailroadSelectModeActive == 1)
	{
		//Choose yer railroad!
		//if (HitSwitches[8][1] == 1) //Left flipper
		//{
		//	ChosenRailroad = 1; //CPRR
		//}
		//if (HitSwitches[8][5] == 1) //Right flipper
		//{
		//	ChosenRailroad = 2; //UPRR
		//}
		if (HitSwitches[8][1] == 1 || HitSwitches[8][5] == 1)
		{
			if (ChosenRailroad == 1)//CPRR
			{
				ChosenRailroad = 2;
			}
			else
			{
				ChosenRailroad = 1;
			}
		}
		if (HitSwitches[8][2] == 1 && CurrentProgramMillis > ImpatientRailroadSelectMillis + 500) //Start button
		{
			//Railroad has been selected; proceed normally
			RailroadSelectModeActive = 0;
			Serial.println(F("Player chose railroad"));
		}
		if (CurrentProgramMillis > ImpatientRailroadSelectMillis + 15000) //15 seconds to choose a RR
		{
			//Player too lazy to select railroad in time, boohoo, life isn't fair, bud
			RailroadSelectModeActive = 0;
			Serial.println(F("Railroad select timed out"));
		}
	}

	if (GameStartState == 1 && RailroadSelectModeActive == 0)
	{
		GameStartMillis = CurrentProgramMillis;
		if (CurrentSwitchState[3][0] == 1 || CurrentSwitchState[3][1] == 1 || CurrentSwitchState[3][2] == 1 || CurrentSwitchState[4][2] == 1)
		{
			GameStartState = 2;
		}
		else
		{
			GameStartState = 3;
		}
	}
	if (GameStartState == 2 && CurrentProgramMillis > GameStartMillis + 500)
	{
		//Drop target reset
		CoilFireFlags[2][7] = 1;

		GameStartState = 3;
		GameStartMillis = CurrentProgramMillis;
	}
	if (GameStartState == 3 && CurrentProgramMillis > GameStartMillis + 500)
	{
		//Launch ball
		GameActive = 1;

		RAILCompleted = 0;
		for (int x = 0; x < 4; x++)
		{
			RAILTriggered[x] = 0;
		}

		CurrentBallNum = 1;
		CoilFireFlags[0][5] = 1; //Eject a ball
		NumBallsInTrough = NumBallsInstalled - 1; //There is a specific reason I did it this way instead of the obvious way.

		NumBallsShouldBeInPlay = 1;

		ShootAgainMillis = CurrentProgramMillis;
		ShootAgainActive = 1;

		BallCounterDisableMillis = CurrentProgramMillis;
		WriteToCoil(22, 1); //Enable the flippers with the flipper relay
		Serial.println(F("Ball 1"));

		GameStartState = 0;
	}

	//Makes the Autofire launch the ball if the player hasn't launched it in the set amount of time.
	if (HitSwitches[6][7] == 1)
	{
		ImpatientAutofireMillis = CurrentProgramMillis;
	}
	if (CurrentSwitchState[6][7] == 1 && CurrentProgramMillis > ImpatientAutofireMillis + ImpatientAutofireInterval)
	{
		CoilFireFlags[2][5] = 1;
	}
	if ((BallJustSaved == 1 || MultiballModeActive == 1) && CurrentSwitchState[6][7] == 1 && CurrentProgramMillis > ImpatientAutofireMillis + 500)
	{
		CoilFireFlags[2][5] = 1;
		BallJustSaved = 0;
	}

	if (HitSwitches[8][2] == 1 && CurrentSwitchState[6][7] == 1) //Does the player want the autofire to launch the ball?
	{
		CoilFireFlags[2][5] = 1;
	}

	if (GameActive == 1)
	{
		//This if statement wraps everything that would happen during a normal game. Makes the program run faster if a game has not been started.
		
		if (EndOfBallBonusStage == 0 && MatchState == 0 && HighScoreMessageState == 0)
		{
			WriteToCoil(22, 1); //To try and fix that bug
		}

		WriteToCoil(7, 1); //Make the start button glow solid while the game is active.

		if (BallCounterDisabledByExecutiveOrder == 0 && CurrentProgramMillis > BallCounterDisableMillis + 1000 && CurrentSwitchState[7][3] == 1) //Is there a jam in the trough?
		{
			BallCounterDisableMillis = CurrentProgramMillis;
			TroughJammed = 1;
			Serial.println(F("There is a jam in the trough."));
			CoilFireFlags[0][5] = 1; //Eject the jam!
			if (MultiballModeActive == 0) //We don't want the flippers disabled during a multiball mode!
			{
				WriteToCoil(22, 0); //Disable the flippers with the flipper relay while jam is being fixed.
			}
		}
		
		//Make the autofire send any jammed balls up the shooter lane and back into the trough.
		if (TroughJammed == 1 && HitSwitches[6][7] == 1)
		{
			ImpatientAutofireMillis = CurrentProgramMillis;
			if (NumBallsInstalled - NumBallsShouldBeInPlay == HowManyBalls())
			{
				TroughJammed = 0;
				Serial.println(F("The jam has been easily cleared."));
				WriteToCoil(22, 1); //Re-enable the flippers
			}
		}
		if (TroughJammed == 1 && CurrentSwitchState[6][7] == 1 && CurrentProgramMillis > ImpatientAutofireMillis + 500)
		{
			CoilFireFlags[2][5] = 1; //Fire the autofire!
		}

		if (BallCounterDisabledByExecutiveOrder == 0 && CurrentProgramMillis > BallCounterDisableMillis + 1000)
		{
			NumBallsInTrough = HowManyBalls();
			NumBallsInPlay = NumBallsInstalled - NumBallsInTrough;
		}

		if (HitSwitches[1][2] == 1) //Pop Bumper 1
		{
			MilesOfTrackLaid += MilesOfTrackPerHit;
			if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += MilesOfTrackPerHit;
			}
			PlayerOneScore += PopBumperScore;

			if (MutuallyExclusiveModeActive == 0)
			{
				TrackProgressMessageState = 1;
				TrackProgressMessageMillis = CurrentProgramMillis;
			}

			CoilFireFlags[0][0] = 1; //Pop 1
		}
		if (HitSwitches[2][3] == 1) //Pop Bumper 2
		{
			MilesOfTrackLaid += MilesOfTrackPerHit;
			if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += MilesOfTrackPerHit;
			}
			PlayerOneScore += PopBumperScore;

			if (MutuallyExclusiveModeActive == 0)
			{
				TrackProgressMessageState = 1;
				TrackProgressMessageMillis = CurrentProgramMillis;
			}

			CoilFireFlags[0][1] = 1; //Pop 2
		}
		if (HitSwitches[2][5] == 1) //Pop Bumper 3
		{
			MilesOfTrackLaid += MilesOfTrackPerHit;
			if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += MilesOfTrackPerHit;
			}
			PlayerOneScore += PopBumperScore;

			if (MutuallyExclusiveModeActive == 0)
			{
				TrackProgressMessageState = 1;
				TrackProgressMessageMillis = CurrentProgramMillis;
			}

			CoilFireFlags[2][0] = 1; //Pop 3
		}
		if (HitSwitches[3][4] == 1) //Pop Bumper 4
		{
			MilesOfTrackLaid += MilesOfTrackPerHit;
			if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += MilesOfTrackPerHit;
			}
			PlayerOneScore += PopBumperScore;

			if (MutuallyExclusiveModeActive == 0)
			{
				TrackProgressMessageState = 1;
				TrackProgressMessageMillis = CurrentProgramMillis;
			}

			CoilFireFlags[2][1] = 1; //Pop 4
		}

		if (HitSwitches[5][4] == 1) //Left slingshot
		{
			MilesOfTrackLaid += MilesOfTrackPerHit;
			if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += MilesOfTrackPerHit;
			}
			PlayerOneScore += SlingshotScore;

			CoilFireFlags[0][4] = 1; //Left slingshot
		}
		if (HitSwitches[5][5] == 1) //Right slingshot
		{
			MilesOfTrackLaid += MilesOfTrackPerHit;
			if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += MilesOfTrackPerHit;
			}
			PlayerOneScore += SlingshotScore;

			CoilFireFlags[2][4] = 1; //Right slingshot
		}

		if (HitSwitches[2][0] == 1) //Left spinner
		{
			MilesOfTrackLaid += MilesOfTrackPerHit;
			if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += MilesOfTrackPerHit;
			}

			if (MutuallyExclusiveModeActive == 0)
			{
				TrackProgressMessageState = 1;
				TrackProgressMessageMillis = CurrentProgramMillis;
			}

			PlayerOneScore += SpinnerScore;
		}
		if (HitSwitches[2][7] == 1) //Right spinner
		{
			MilesOfTrackLaid += MilesOfTrackPerHit;
			if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += MilesOfTrackPerHit;
			}

			if (MutuallyExclusiveModeActive == 0)
			{
				TrackProgressMessageState = 1;
				TrackProgressMessageMillis = CurrentProgramMillis;
			}

			PlayerOneScore += SpinnerScore;
		}

		if (HitSwitches[4][5] == 1) //"S"team
		{
			PlayerOneScore += OutlaneScore;
			STEAMTriggered[0] = 1;
		}
		if (HitSwitches[4][6] == 1) //s"T"eam
		{
			PlayerOneScore += ReturnLaneScore;
			STEAMTriggered[1] = 1;
		}
		if (HitSwitches[4][7] == 1) //st"E"am
		{
			PlayerOneScore += ReturnLaneScore;
			STEAMTriggered[2] = 1;
		}
		if (HitSwitches[5][6] == 1) //ste"A"m
		{
			PlayerOneScore += ReturnLaneScore;
			STEAMTriggered[3] = 1;
		}
		if (HitSwitches[5][7] == 1) //stea"M"
		{
			PlayerOneScore += OutlaneScore;
			STEAMTriggered[4] = 1;
		}

		if (STEAMTriggered[0] == 1 && STEAMTriggered[1] == 1 && STEAMTriggered[2] == 1 && STEAMTriggered[3] == 1 && STEAMTriggered[4] == 1) //Has STEAM been completed?
		{
			STEAMCompleted = 1;

			STEAMTriggered[0] = 0;
			STEAMTriggered[1] = 0;
			STEAMTriggered[2] = 0;
			STEAMTriggered[3] = 0;
			STEAMTriggered[4] = 0;
			STEAMTriggered[5] = 0;

			PlayerOneScore += STEAMCompletedScore;
		}

		if (HitSwitches[0][3] == 1) //"T"crr
		{
			PlayerOneScore += TCRRLaneScore;
			TCRRTriggered[0] = 1;
		}
		if (HitSwitches[0][4] == 1) //t"C"rr
		{
			PlayerOneScore += TCRRLaneScore;
			TCRRTriggered[1] = 1;
		}
		if (HitSwitches[0][5] == 1) //tc"R"r
		{
			PlayerOneScore += TCRRLaneScore;
			TCRRTriggered[2] = 1;
		}
		if (HitSwitches[0][6] == 1) //tcr"R"
		{
			PlayerOneScore += TCRRLaneScore;
			TCRRTriggered[3] = 1;
		}

		if (TCRRTriggered[0] == 1 && TCRRTriggered[1] == 1 && TCRRTriggered[2] == 1 && TCRRTriggered[3] == 1)
		{
			TCRRCompleted = 1;

			TCRRTriggered[0] = 0;
			TCRRTriggered[1] = 0;
			TCRRTriggered[2] = 0;
			TCRRTriggered[3] = 0;
			TCRRTriggered[4] = 0;

			PlayerOneScore += TCRRCompletedScore;
		}

		if (HitSwitches[8][1] == 1 && HighScoreMessageState == 0) //Left flipper button pressed
		{
			//Rotate 'em
			TCRRTriggered[4] = TCRRTriggered[0];
			TCRRTriggered[0] = TCRRTriggered[1];
			TCRRTriggered[1] = TCRRTriggered[2];
			TCRRTriggered[2] = TCRRTriggered[3];
			TCRRTriggered[3] = TCRRTriggered[4];

			STEAMTriggered[5] = STEAMTriggered[0];
			STEAMTriggered[0] = STEAMTriggered[1];
			STEAMTriggered[1] = STEAMTriggered[2];
			STEAMTriggered[2] = STEAMTriggered[3];
			STEAMTriggered[3] = STEAMTriggered[4];
			STEAMTriggered[4] = STEAMTriggered[5];
		}
		if (HitSwitches[8][5] == 1 && HighScoreMessageState == 0) //Right flipper button pressed
		{
			TCRRTriggered[4] = TCRRTriggered[3];
			TCRRTriggered[3] = TCRRTriggered[2];
			TCRRTriggered[2] = TCRRTriggered[1];
			TCRRTriggered[1] = TCRRTriggered[0];
			TCRRTriggered[0] = TCRRTriggered[4];

			STEAMTriggered[5] = STEAMTriggered[4];
			STEAMTriggered[4] = STEAMTriggered[3];
			STEAMTriggered[3] = STEAMTriggered[2];
			STEAMTriggered[2] = STEAMTriggered[1];
			STEAMTriggered[1] = STEAMTriggered[0];
			STEAMTriggered[0] = STEAMTriggered[5];
		}

		if (HitSwitches[5][3] == 1) //"E"ngine
		{
			ENGINETriggered[0] = 1;
			PlayerOneScore += ENGINELetterScore;
		}
		if (HitSwitches[5][2] == 1) //e"N"gine
		{
			ENGINETriggered[1] = 1;
			PlayerOneScore += ENGINELetterScore;
		}
		if (HitSwitches[5][1] == 1) //en"G"ine
		{
			ENGINETriggered[2] = 1;
			PlayerOneScore += ENGINELetterScore;
		}
		if (HitSwitches[5][0] == 1) //eng"I"ne
		{
			ENGINETriggered[3] = 1;
			PlayerOneScore += ENGINELetterScore;
		}
		if (HitSwitches[4][0] == 1) //engi"N"e
		{
			ENGINETriggered[4] = 1;
			PlayerOneScore += ENGINELetterScore;
		}
		if (HitSwitches[4][1] == 1) //engin"E"
		{
			ENGINETriggered[5] = 1;
			PlayerOneScore += ENGINELetterScore;
		}

		if (ENGINETriggered[0] == 1 && ENGINETriggered[1] == 1 && ENGINETriggered[2] == 1 && ENGINETriggered[3] == 1 && ENGINETriggered[4] == 1 && ENGINETriggered[5] == 1)
		{
			ENGINECompleted = 1;

			ENGINETriggered[0] = 0;
			ENGINETriggered[1] = 0;
			ENGINETriggered[2] = 0;
			ENGINETriggered[3] = 0;
			ENGINETriggered[4] = 0;
			ENGINETriggered[5] = 0;

			NumberOfEnginesAcquired++;

			PlayerOneScore += ENGINECompletedScore;

			ENGINECompletedMillis = CurrentProgramMillis;
		}

		if (HitSwitches[2][1] == 1) //"I"ndians
		{
			INDIANSTriggered[0] = 1;
			if (INDIANSModeActive == 1)
			{
				PlayerOneScore += INDIANSModeLetterScore;
			}
			else
			{
				PlayerOneScore += INDIANSNormalLetterScore;
			}
		}
		if (HitSwitches[2][2] == 1) //i"N"dians
		{
			INDIANSTriggered[1] = 1;
			if (INDIANSModeActive == 1)
			{
				PlayerOneScore += INDIANSModeLetterScore;
			}
			else
			{
				PlayerOneScore += INDIANSNormalLetterScore;
			}
		}
		if (HitSwitches[3][3] == 1) //in"D"ians
		{
			INDIANSTriggered[2] = 1;
			if (INDIANSModeActive == 1)
			{
				PlayerOneScore += INDIANSModeLetterScore;
			}
			else
			{
				PlayerOneScore += INDIANSNormalLetterScore;
			}
		}
		if (HitSwitches[3][5] == 1) //ind"I"ans
		{
			INDIANSTriggered[3] = 1;
			if (INDIANSModeActive == 1)
			{
				PlayerOneScore += INDIANSModeLetterScore;
			}
			else
			{
				PlayerOneScore += INDIANSNormalLetterScore;
			}
		}
		if (HitSwitches[2][6] == 1) //indi"A"ns
		{
			INDIANSTriggered[4] = 1;
			if (INDIANSModeActive == 1)
			{
				PlayerOneScore += INDIANSModeLetterScore;
			}
			else
			{
				PlayerOneScore += INDIANSNormalLetterScore;
			}
		}
		if (HitSwitches[3][6] == 1) //india"N"s
		{
			INDIANSTriggered[5] = 1;
			if (INDIANSModeActive == 1)
			{
				PlayerOneScore += INDIANSModeLetterScore;
			}
			else
			{
				PlayerOneScore += INDIANSNormalLetterScore;
			}
		}
		if (HitSwitches[3][7] == 1) //indian"S"
		{
			INDIANSTriggered[6] = 1;
			if (INDIANSModeActive == 1)
			{
				PlayerOneScore += INDIANSModeLetterScore;
			}
			else
			{
				PlayerOneScore += INDIANSNormalLetterScore;
			}
		}

		if (INDIANSTriggered[0] == 1 && INDIANSTriggered[1] == 1 && INDIANSTriggered[2] == 1 && INDIANSTriggered[3] == 1 && INDIANSTriggered[4] == 1 && INDIANSTriggered[5] == 1 && INDIANSTriggered[6] == 1)
		{
			INDIANSTriggered[0] = 0;
			INDIANSTriggered[1] = 0;
			INDIANSTriggered[2] = 0;
			INDIANSTriggered[3] = 0;
			INDIANSTriggered[4] = 0;
			INDIANSTriggered[5] = 0;
			INDIANSTriggered[6] = 0;

			INDIANSCompleted = 1;
			INDIANSCompletedMillis = CurrentProgramMillis;

			PlayerOneScore += INDIANSModeCompletedScore;

			if (INDIANSModeActive == 1)
			{
				INDIANSGoalCompleted = 1;

				INDIANSModeMessageMillis = CurrentProgramMillis;
				INDIANSModeMessageState = 4;

				DataToSend[15] = 9;
				DataToSend[16] = 4;
			}
		}

		if (CurrentSwitchState[3][0] == 1 && RAILTriggered[0] == 0 && DropTargetBeingDebounced == 0) //"R"ail
		{
			RAILTriggered[0] = 1;
			PlayerOneScore += RAILNormalLetterScore;

			UpperLEDColor[22] = 8;
			UpperLEDState[22] = 1;
		}
		else if (CurrentSwitchState[3][0] == 0)
		{
			RAILTriggered[0] = 0;
			UpperLEDState[22] = 0;
		}

		if (CurrentSwitchState[3][1] == 1 && RAILTriggered[1] == 0 && DropTargetBeingDebounced == 0) //r"A"il
		{
			RAILTriggered[1] = 1;
			PlayerOneScore += RAILNormalLetterScore;

			UpperLEDColor[23] = 8;
			UpperLEDState[23] = 1;
		}
		else if (CurrentSwitchState[3][1] == 0)
		{
			RAILTriggered[1] = 0;
			UpperLEDState[23] = 0;
		}

		if (CurrentSwitchState[3][2] == 1 && RAILTriggered[2] == 0 && DropTargetBeingDebounced == 0) //ra"I"l
		{
			RAILTriggered[2] = 1;
			PlayerOneScore += RAILNormalLetterScore;

			UpperLEDColor[24] = 8;
			UpperLEDState[24] = 1;
		}
		else if (CurrentSwitchState[3][2] == 0)
		{
			RAILTriggered[2] = 0;
			UpperLEDState[24] = 0;
		}

		if (CurrentSwitchState[4][2] == 1 && RAILTriggered[3] == 0 && DropTargetBeingDebounced == 0) //rai"L"
		{
			RAILTriggered[3] = 1;
			PlayerOneScore += RAILNormalLetterScore;

			UpperLEDColor[25] = 8;
			UpperLEDState[25] = 1;
		}
		else if (CurrentSwitchState[4][2] == 0)
		{
			RAILTriggered[3] = 0;
			UpperLEDState[25] = 0;
		}

		if (RAILCompleted == 0 && RAILTriggered[0] == 1 && RAILTriggered[1] == 1 && RAILTriggered[2] == 1 && RAILTriggered[3] == 1 && DropTargetNeedsToBeReset == 0)
		{
			RAILCompleted = 1;
		}
		if (DropTargetNeedsToBeReset == 1) //Make sure the RAIL lights aren't lit after INDIANS mode
		{
			UpperLEDState[22] = 0;
			UpperLEDState[23] = 0;
			UpperLEDState[24] = 0;
			UpperLEDState[25] = 0;
		}

		if (RAILCompleted == 1 && MutuallyExclusiveModeActive == 0)
		{
			UpperLEDColor[17] = 1;
			UpperLEDState[17] = 2;
			UpperLEDSpeed[17] = 200;
		}
		else
		{
			UpperLEDState[17] = 0;
		}

		if (INDIANSModeActive == 1 && INDIANSModeStage == 1 && CurrentProgramMillis > INDIANSModeMillis + 4500)
		{
			NumBallsShouldBeInPlay = 2; //2-ball multiball

			CoilFireFlags[0][5] = 1; //Eject a ball
			BallCounterDisableMillis = CurrentProgramMillis;
			NumBallsInTrough--;

			INDIANSModeStage = 2;
		}

		if (DropTargetNeedsToBeReset == 1 && WasAnySwitchOtherThanThisOneHit(7, 4) == 1)
		{
			DropTargetBeingDebounced = 1;
			DropTargetNeedsToBeReset = 2;
			Serial.println(F("Drop target bank ready to be reset."));
			DropTargetResetMillis = CurrentProgramMillis;
		}
		if (DropTargetNeedsToBeReset == 2 && CurrentProgramMillis > DropTargetResetMillis + 250)
		{
			CoilFireFlags[2][7] = 1;
			Serial.println(F("Drop target bank has been reset"));
			DropTargetNeedsToBeReset = 3;
		}
		if(DropTargetNeedsToBeReset == 3 && CurrentProgramMillis > DropTargetResetMillis + 750)
		{
			DropTargetNeedsToBeReset = 0;
			DropTargetBeingDebounced = 0;

			RAILTriggered[0] = 0;
			RAILTriggered[1] = 0;
			RAILTriggered[2] = 0;
			RAILTriggered[3] = 0;

			UpperLEDState[22] = 0;
			UpperLEDState[23] = 0;
			UpperLEDState[24] = 0;
			UpperLEDState[25] = 0;

			RAILCompleted = 0;
		}

		if (CurrentSwitchState[7][4] == 1 && CurrentProgramMillis > VUKTriggeredMillis[0] + 2000 && VUKTriggered[0] == 0) //Indians kicker
		{
			VUKTriggered[0] = 1;
			VUKTriggeredMillis[0] = CurrentProgramMillis;

			PlayerOneScore += INDIANSKickerScore;

			if (RAILCompleted == 1 && INDIANSModeActive == 0 && MutuallyExclusiveModeActive == 0)
			{
				INDIANSModeStage = 1;
				INDIANSModeActive = 1;
				INDIANSModeMillis = CurrentProgramMillis;
				ShootAgainMillis = CurrentProgramMillis;
				ShootAgainActive = 1;

				MutuallyExclusiveModeActive = 1;
				MultiballModeActive = 1;

				INDIANSTriggered[0] = 0;
				INDIANSTriggered[1] = 0;
				INDIANSTriggered[2] = 0;
				INDIANSTriggered[3] = 0;
				INDIANSTriggered[4] = 0;
				INDIANSTriggered[5] = 0;
				INDIANSTriggered[6] = 0;

				UpperLEDState[7] = 0;
				UpperLEDState[8] = 0;
				UpperLEDState[12] = 0;
				UpperLEDState[13] = 0;
				UpperLEDState[15] = 0;
				UpperLEDState[16] = 0;
				UpperLEDState[18] = 0;

				UpperLEDState[17] = 0;

				DataToSend[15] = 9;
				DataToSend[16] = 1;
				INDIANSModeMessageState = 1;
			}
		}
		if (VUKTriggered[0] == 1 && CurrentProgramMillis > VUKTriggeredMillis[0] + 2000 && INDIANSModeMessageState == 1)
		{
			DataToSend[15] = 9;
			DataToSend[16] = 2;
			INDIANSModeMessageState = 2;

			VUKTriggeredMillis[0] = CurrentProgramMillis;
		}

		if (VUKTriggered[0] == 1 && CurrentProgramMillis > VUKTriggeredMillis[0] + 3000 && INDIANSModeMessageState == 2)
		{
			CoilFireFlags[0][2] = 1;
			VUKTriggered[0] = 0;

			VUKTriggeredMillis[0] = CurrentProgramMillis;

			INDIANSModeMessageState = 3;
		}
		if (VUKTriggered[0] == 1 && CurrentProgramMillis > VUKTriggeredMillis[0] + 1000 && INDIANSModeMessageState != 0 && INDIANSModeMessageState != 1 && INDIANSModeMessageState != 2)
		{
			CoilFireFlags[0][2] = 1;
			VUKTriggered[0] = 0;

			VUKTriggeredMillis[0] = CurrentProgramMillis;
		}
		if (VUKTriggered[0] == 1 && CurrentProgramMillis > VUKTriggeredMillis[0] + 1000 && INDIANSModeActive == 0)
		{
			CoilFireFlags[0][2] = 1;
			VUKTriggered[0] = 0;

			VUKTriggeredMillis[0] = CurrentProgramMillis;
		}
		
		if (MutuallyExclusiveModeActive == 0 && PlayerOneScore > LastExtraBallScore + ExtraBallAwardedAtPointScore && ExtraBallMessageState != 1 && ExtraBallMessageState != 2 && EndOfBallBonusStage == 0 && MatchState == 0 && HighScoreMessageState == 0)
		{
			ExtraBallMessageMillis = CurrentProgramMillis;
			ExtraBallMessageState = 1;

			DataToSend[15] = 11;
		}

		if (CurrentSwitchState[7][5] == 1 && CurrentProgramMillis > VUKTriggeredMillis[1] + 2000 && VUKTriggered[1] == 0) //Supply tower kicker
		{
			VUKTriggered[1] = 1;
			VUKTriggeredMillis[1] = CurrentProgramMillis;
			
			if (MutuallyExclusiveModeActive == 0 && RailroadCompleted == 1)
			{
				DataToSend[15] = 5;
				DataToSend[17] = ChosenRailroad;

				if (ChosenRailroad == 1) //CPRR
				{
					CentralPacificCompleted = 1;
					ChosenRailroad = 2;
				}
				else if (ChosenRailroad == 2) //UPRR
				{
					UnionPacificCompleted = 1;
					ChosenRailroad = 1;
				}
				PlayerOneScore += RailroadCompletionScoreBonus;

				//Starting over on the new RR!
				MilesOfTrackLaid = 0;
				TrackFrenziesCompleted = 0;
				NumberOfEnginesAcquired = 0;
				NumberOfIndianSquabbles = 0;
				NumberOfTrestlesBuilt = 0;
				NumberOfTunnelsBlasted = 0;
				PartialTrestlesCompleted = 0;
				PartialTunnelsCompleted = 0;

				RailroadCompleted = 0;

				TrackFrenzyHappenedYet = 0;

				//But give the player a nice multiball mode to get it started!
				RailroadCompletedMultiballStage = 1;
				RailroadCompletedMultiballModeActive = 1;
				RailroadCompletedMultiballMillis = CurrentProgramMillis;
				MutuallyExclusiveModeActive = 1;
				MultiballModeActive = 1;

				SupplyTowerRewardThisLoop = 8;

				return;
			}
			else if (MutuallyExclusiveModeActive == 0 && TrackFrenzyHappenedYet == 0) //Shooting the supply tower gives the track frenzy multiball the first time.
			{
				TrackFrenzyModeActive = 1;
				TrackFrenzyModeMillis = CurrentProgramMillis;
				TrackFrenzyModeStage = 1;
				TrackFrenzyHappenedYet = 1;

				MutuallyExclusiveModeActive = 1;

				MultiballModeActive = 1;

				DataToSend[15] = 3;
				DataToSend[16] = 6;

				SupplyTowerRewardThisLoop = 6;
			}
			else if (TrackFrenzyModeActive == 1)
			{
				MilesOfTrackBuiltDuringFrenzy += SupplyTowerTrackAwardDuringFrenzy;
				MilesOfTrackLaid += SupplyTowerTrackAwardDuringFrenzy;
			}
			else if (PlayerOneScore >= LastExtraBallScore + ExtraBallAwardedAtPointScore)
			{
				ExtraBallsEarned++;
				DataToSend[15] = 3;
				DataToSend[16] = 7;

				LastExtraBallScore += ExtraBallAwardedAtPointScore;

				ExtraBallMessageState = 0;

				SupplyTowerRewardThisLoop = 7;
			}
			else if (MutuallyExclusiveModeActive == 0 && TrackFrenziesCompleted == 0)
			{
				//If a track frenzy has not been successfully completed, the odds of getting one as a reward are much higher.
				//REWARDS:
				//	x bonus points           32%
				//  x amount of bonus track  23%
				//  Extra locomotive          8%
				//  Extra Trestle             8%
				//  Extra Tunnel              8%
				//  Track frenzy multiball   20%
				//  Extra ball                1%

				DataToSend[15] = 3;

				RandomlyGeneratedNumber = random(0, 100); //Returns between 0 and 99
				switch (RandomlyGeneratedNumber) //The idea is the switch case falls through cases until it finds the reward.
				{
				case 0:
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
				case 6:
				case 7:
				case 8:
				case 9:
				case 10:
				case 11:
				case 12:
				case 13:
				case 14:
				case 15:
				case 16:
				case 17:
				case 18:
				case 19:
				case 20:
				case 21:
				case 22:
				case 23:
				case 24:
				case 25:
				case 26:
				case 27:
				case 28:
				case 29:
				case 30:
				case 31:
					PlayerOneScore += SupplyTowerPointBonusReward;
					DataToSend[16] = 1;
					MathVariable = SupplyTowerPointBonusReward;
					MathVariableTwo = 0;
					while (MathVariable > 255)
					{
						MathVariable -= 256;
						MathVariableTwo++;
					}
					DataToSend[17] = MathVariableTwo;
					DataToSend[18] = MathVariable;

					SupplyTowerRewardThisLoop = 1;
					break;
				case 32:
				case 33:
				case 34:
				case 35:
				case 36:
				case 37:
				case 38:
				case 39:
				case 40:
				case 41:
				case 42:
				case 43:
				case 44:
				case 45:
				case 46:
				case 47:
				case 48:
				case 49:
				case 50:
				case 51:
				case 52:
				case 53:
				case 54:
					MilesOfTrackLaid += SupplyTowerMilesOfTrackReward;
					DataToSend[16] = 2;
					DataToSend[17] = SupplyTowerMilesOfTrackReward;

					SupplyTowerRewardThisLoop = 2;
					break;
				case 55:
				case 56:
				case 57:
				case 58:
				case 59:
				case 60:
				case 61:
				case 62:
					NumberOfEnginesAcquired++;
					DataToSend[16] = 3;

					SupplyTowerRewardThisLoop = 3;
					break;
				case 63:
				case 64:
				case 65:
				case 66:
				case 67:
				case 68:
				case 69:
				case 70:
					NumberOfTrestlesBuilt++;
					DataToSend[16] = 4;

					SupplyTowerRewardThisLoop = 4;
					break;
				case 71:
				case 72:
				case 73:
				case 74:
				case 75:
				case 76:
				case 77:
				case 78:
					NumberOfTunnelsBlasted++;
					DataToSend[16] = 5;

					SupplyTowerRewardThisLoop = 5;
					break;
				case 79:
				case 80:
				case 81:
				case 82:
				case 83:
				case 84:
				case 85:
				case 86:
				case 87:
				case 88:
				case 89:
				case 90:
				case 91:
				case 92:
				case 93:
				case 94:
				case 95:
				case 96:
				case 97:
				case 98:
					//Track frenzy multiball stuff
					TrackFrenzyModeActive = 1;
					TrackFrenzyModeMillis = CurrentProgramMillis;
					TrackFrenzyModeStage = 1;
					TrackFrenzyHappenedYet = 1;

					MutuallyExclusiveModeActive = 1;

					MultiballModeActive = 1;

					DataToSend[16] = 6;

					SupplyTowerRewardThisLoop = 6;

					break;
				case 99:
					ExtraBallsEarned++;
					DataToSend[16] = 7;

					SupplyTowerRewardThisLoop = 7;
					break;
				default:
					PlayerOneScore += SupplyTowerPointBonusReward;
					DataToSend[16] = 1;

					SupplyTowerRewardThisLoop = 1;
					break;
				}
			}
			else if (MutuallyExclusiveModeActive == 0)
			{
				//Proceed with the random reward from the supply tower
				//REWARDS: (most common to least common)
				//	x bonus points           40%
				//  x amount of bonus track  30%
				//  Extra locomotive          8%   
				//  Extra Trestle             8%
				//  Extra Tunnel              8%
				//  Track frenzy multiball    5%
				//  Extra ball                1%
				
				DataToSend[15] = 3;
				
				RandomlyGeneratedNumber = random(0, 100); //Returns between 0 and 99
				switch (RandomlyGeneratedNumber) //The idea is the switch case falls through cases until it finds the reward.
				{
				case 0:
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
				case 6:
				case 7:
				case 8:
				case 9:
				case 10:
				case 11:
				case 12:
				case 13:
				case 14:
				case 15:
				case 16:
				case 17:
				case 18:
				case 19:
				case 20:
				case 21:
				case 22:
				case 23:
				case 24:
				case 25:
				case 26:
				case 27:
				case 28:
				case 29:
				case 30:
				case 31:
				case 32:
				case 33:
				case 34:
				case 35:
				case 36:
				case 37:
				case 38:
				case 39:
					PlayerOneScore += SupplyTowerPointBonusReward;
					DataToSend[16] = 1;
					MathVariable = SupplyTowerPointBonusReward;
					MathVariableTwo = 0;
					while (MathVariable > 255)
					{
						MathVariable -= 256;
						MathVariableTwo++;
					}
					DataToSend[17] = MathVariableTwo;
					DataToSend[18] = MathVariable;

					SupplyTowerRewardThisLoop = 1;
					break;
				case 40:
				case 41:
				case 42:
				case 43:
				case 44:
				case 45:
				case 46:
				case 47:
				case 48:
				case 49:
				case 50:
				case 51:
				case 52:
				case 53:
				case 54:
				case 55:
				case 56:
				case 57:
				case 58:
				case 59:
				case 60:
				case 61:
				case 62:
				case 63:
				case 64:
				case 65:
				case 66:
				case 67:
				case 68:
				case 69:
					MilesOfTrackLaid += SupplyTowerMilesOfTrackReward;
					DataToSend[16] = 2;
					DataToSend[17] = SupplyTowerMilesOfTrackReward;

					SupplyTowerRewardThisLoop = 2;
					break;
				case 70:
				case 71:
				case 72:
				case 73:
				case 74:
				case 75:
				case 76:
				case 77:
					NumberOfEnginesAcquired++;
					DataToSend[16] = 3;

					SupplyTowerRewardThisLoop = 3;
					break;
				case 78:
				case 79:
				case 80:
				case 81:
				case 82:
				case 83:
				case 84:
				case 85:
				case 86:
					NumberOfTrestlesBuilt++;
					DataToSend[16] = 4;

					SupplyTowerRewardThisLoop = 4;
					break;
				case 87:
				case 88:
				case 89:
				case 90:
				case 91:
				case 92:
				case 93:
					NumberOfTunnelsBlasted++;
					DataToSend[16] = 5;

					SupplyTowerRewardThisLoop = 5;
					break;
				case 94:
				case 95:
				case 96:
				case 97:
				case 98:
					//Track frenzy multiball
					TrackFrenzyModeActive = 1;
					TrackFrenzyModeMillis = CurrentProgramMillis;
					TrackFrenzyModeStage = 1;
					TrackFrenzyHappenedYet = 1;

					MutuallyExclusiveModeActive = 1;

					MultiballModeActive = 1;

					DataToSend[16] = 6;

					SupplyTowerRewardThisLoop = 6;

					break;
				case 99:
					ExtraBallsEarned++;
					DataToSend[16] = 7;

					SupplyTowerRewardThisLoop = 7;
					break;
				default:
					PlayerOneScore += SupplyTowerPointBonusReward;

					SupplyTowerRewardThisLoop = 1;
					break;
				}
			}
			else
			{
				PlayerOneScore += SupplyTowerHitScore;
			}
		}
		if (VUKTriggered[1] == 1 && CurrentProgramMillis > VUKTriggeredMillis[1] + 1000 && SupplyTowerRewardThisLoop == 0)
		{
			CoilFireFlags[0][3] = 1;
			VUKTriggered[1] = 0;
			VUKTriggeredMillis[1] = CurrentProgramMillis;
		}
		if (VUKTriggered[1] == 1 && CurrentProgramMillis > VUKTriggeredMillis[1] + 3500 && SupplyTowerRewardThisLoop != 0 && RailroadCompletedMultiballModeActive == 0)
		{
			if (DataToSend[15] == 4 && TrackFrenzyModeActive == 1)
			{
				CoilFireFlags[0][3] = 1;
				VUKTriggered[1] = 0;
				VUKTriggeredMillis[1] = CurrentProgramMillis;

				SupplyTowerRewardThisLoop = 0;
			}
			else if (TrackFrenzyModeActive == 0)
			{
				CoilFireFlags[0][3] = 1;
				VUKTriggered[1] = 0;
				VUKTriggeredMillis[1] = CurrentProgramMillis;

				SupplyTowerRewardThisLoop = 0;
			}
			
			if (MultiballModeActive == 0)
			{
				DataToSend[15] = 0;
			}
			else if (TrackFrenzyModeActive == 1 && DataToSend[15] != 4)
			{
				DataToSend[15] = 4;
				VUKTriggeredMillis[1] = CurrentProgramMillis;
			}
		}
		if (VUKTriggered[1] == 1 && CurrentProgramMillis > VUKTriggeredMillis[1] + 5000 && SupplyTowerRewardThisLoop != 0 && RailroadCompletedMultiballModeActive == 1)
		{
			CoilFireFlags[0][3] = 1;
			VUKTriggered[1] = 0;
			VUKTriggeredMillis[1] = CurrentProgramMillis;

			SupplyTowerRewardThisLoop = 0;

			DataToSend[15] = 0;
		}

		if (TrackFrenzyModeStage == 1 && CurrentProgramMillis > TrackFrenzyModeMillis + 6500 && MutuallyExclusiveModeActive == 1)
		{
			NumBallsShouldBeInPlay = 2;
			
			CoilFireFlags[0][5] = 1; //Eject a ball
			BallCounterDisableMillis = CurrentProgramMillis;
			NumBallsInTrough--;

			TrackFrenzyModeStage = 0;

			ShootAgainActive = 1;
			ShootAgainMillis = CurrentProgramMillis;
		}

		//Railroad Completed Multiball Mode
		if (RailroadCompletedMultiballModeActive == 1 && RailroadCompletedMultiballStage == 1 && CurrentProgramMillis > RailroadCompletedMultiballMillis + 4500)
		{
			NumBallsShouldBeInPlay = 2;

			CoilFireFlags[0][5] = 1; //Eject a ball
			BallCounterDisableMillis = CurrentProgramMillis;
			NumBallsInTrough--;

			RailroadCompletedMultiballStage = 2;
		}
		if (RailroadCompletedMultiballModeActive == 1 && RailroadCompletedMultiballStage == 2 && CurrentProgramMillis > RailroadCompletedMultiballMillis + 5500)
		{
			NumBallsShouldBeInPlay = 3;

			CoilFireFlags[0][5] = 1; //Eject a ball
			BallCounterDisableMillis = CurrentProgramMillis;
			NumBallsInTrough--;

			ShootAgainActive = 1;
			ShootAgainMillis = CurrentProgramMillis;

			RailroadCompletedMultiballStage = 0;
		}

		//Make sure the messages are being sent; sometimes it happens to be that messages are missed.
		if (SupplyTowerRewardThisLoop != 0 && MutuallyExclusiveModeActive == 0)
		{
			DataToSend[15] = 3;
			switch (SupplyTowerRewardThisLoop)
			{
			case 1:
				DataToSend[16] = 1;
				break;
			case 2:
				DataToSend[16] = 2;
				break;
			case 3:
				DataToSend[16] = 3;
				break;
			case 4:
				DataToSend[16] = 4;
				break;
			case 5:
				DataToSend[16] = 5;
				break;
			case 6:
				DataToSend[16] = 6;
				break;
			case 7:
				DataToSend[16] = 7;
				break;
			case 8:
				DataToSend[15] = 5;
				DataToSend[17] = ChosenRailroad;
				break;
			}
		}
		if (TrackFrenzyModeActive == 1)
		{
			DataToSend[15] == 4;
		}

		if (HitSwitches[2][6] == 1) //Trestle diverter, also technically indi"A"ns
		{
			/*if (DiverterOpen == 1)
			{
				WriteToCoil(6, 0);
				DiverterOpen = 0;
				ImpatientDiverterMillis = CurrentProgramMillis - 15000;
			}*/ //Add an else to the below if uncommented.
			if(DiverterOpen == 0 && INDIANSModeActive == 0 && (CurrentProgramMillis > ImpatientDiverterMillis + 20000 || DiverterOpenedYet == 0))
			{
				ImpatientDiverterMillis = CurrentProgramMillis;
				DiverterOpen = 1;
				DiverterOpenedYet = 1;

				TrestleMessageState = 1;
				TrestleMessageMillis = CurrentProgramMillis;

				if (MutuallyExclusiveModeActive == 0)
				{
					DataToSend[15] = 7;
					DataToSend[16] = 1;
				}

				WriteToCoil(6, 1);
			}
		}
		if (TrestleMessageState == 1 && CurrentProgramMillis > TrestleMessageMillis + 3000)
		{
			if (MutuallyExclusiveModeActive == 0)
			{
				DataToSend[15] = 0;
			}
			TrestleMessageState = 0;
		}


		if(EndOfBallBonusStage != 0 || INDIANSModeActive == 1 || (DiverterOpen == 1 && CurrentProgramMillis > ImpatientDiverterMillis + 120000 && WasAnySwitchOtherThanThisOneHit(7, 6) == 1 && MutuallyExclusiveModeActive == 0))
		{
			WriteToCoil(6, 0);
			DiverterOpen = 0;
			ImpatientDiverterMillis = CurrentProgramMillis;
		}

		if (CurrentSwitchState[7][6] == 1 && CurrentProgramMillis > VUKTriggeredMillis[2] + 2500 && VUKTriggered[2] == 0) //Trestle kicker
		{
			VUKTriggered[2] = 1;
			VUKTriggeredMillis[2] = CurrentProgramMillis;

			if (MutuallyExclusiveModeActive == 0)
			{
				DataToSend[15] = 7; //Trestle
				DataToSend[16] = 2; //Shoot again to build a trestle
				TrestleMessageState = 2;
			}

			PartialTrestlesCompleted++;
			if (PartialTrestlesCompleted > 1) //have 2 partial trestles been built?
			{
				PartialTrestlesCompleted -= 2;
				NumberOfTrestlesBuilt++;
				PlayerOneScore += TrestleBuiltScore;

				WriteToCoil(6, 0);
				DiverterOpen = 0;

				if (MutuallyExclusiveModeActive == 0)
				{
					DataToSend[15] = 7; //Trestle
					DataToSend[16] = 3; //A trestle has been built
					TrestleMessageState = 2;
				}
			}
			else
			{
				PlayerOneScore += PartialTrestleScore;
			}
		}
		if (VUKTriggered[2] == 1 && CurrentProgramMillis > VUKTriggeredMillis[2] + 1500 && MutuallyExclusiveModeActive == 1)
		{
			CoilFireFlags[2][2] = 1;
			VUKTriggered[2] = 0;

			VUKTriggeredMillis[2] = CurrentProgramMillis;
		}
		if (VUKTriggered[2] == 1 && CurrentProgramMillis > VUKTriggeredMillis[2] + 3000 && MutuallyExclusiveModeActive == 0)
		{
			if (ChosenRailroad == 1 && TrestleMessageState != 4) //CPRR
			{
				if (NumberOfTrestlesBuilt >= CPRRTrestlesBuilt)
				{
					DataToSend[15] = 0;
				}
				else if (PartialTrestlesCompleted != 0)
				{
					DataToSend[15] = 0;
				}
				else
				{
					DataToSend[15] = 7; //Trestle
					DataToSend[16] = 4; //You need to build X more trestles
					DataToSend[17] = CPRRTrestlesBuilt - NumberOfTrestlesBuilt;
					TrestleMessageState = 3;
				}
			}
			else if (ChosenRailroad == 2 && TrestleMessageState != 4) //UPRR
			{
				if (NumberOfTrestlesBuilt >= UPRRTrestlesBuilt)
				{
					DataToSend[15] = 0;
				}
				else if (PartialTrestlesCompleted != 0)
				{
					DataToSend[15] = 0;
				}
				else
				{
					DataToSend[15] = 7; //Trestle
					DataToSend[16] = 4; //You need to build X more trestles
					DataToSend[17] = UPRRTrestlesBuilt - NumberOfTrestlesBuilt;
					TrestleMessageState = 3;
				}
			}
			
			if (DataToSend[16] == 4 && TrestleMessageState == 3)
			{
				TrestleMessageState = 4;
				VUKTriggeredMillis[2] = CurrentProgramMillis;
			}
			else
			{
				CoilFireFlags[2][2] = 1;
				VUKTriggered[2] = 0;

				DataToSend[15] = 0;
				TrestleMessageState = 0;

				VUKTriggeredMillis[2] = CurrentProgramMillis;
			}
		}

		if (HitSwitches[3][6] == 1)//Left nitroglycerine, also india"N"s
		{
			NitroglycerineHit[0] = 1;
		}
		if (HitSwitches[3][7] == 1)//Right nitroglycerine, also indian"S"
		{
			NitroglycerineHit[1] = 1;
		}
		if (CurrentSwitchState[7][7] == 1 && CurrentProgramMillis > VUKTriggeredMillis[3] + 2000 && VUKTriggered[3] == 0) //Tunnel kicker
		{
			VUKTriggered[3] = 1;
			VUKTriggeredMillis[3] = CurrentProgramMillis;

			PartialTunnelsCompleted++;

			if (MutuallyExclusiveModeActive == 0)
			{
				DataToSend[15] = 6; //Tunnel
				DataToSend[16] = 1; //Shoot again to blast a tunnel
			}

			if (PartialTunnelsCompleted > 1) //2 partial tunnels completed?
			{
				PartialTunnelsCompleted -= 2;
				NumberOfTunnelsBlasted++;
				if (NitroglycerineHit[0] == 1 && NitroglycerineHit[1] == 1)
				{
					PlayerOneScore += TunnelBlastedScore;
					PlayerOneScore += TunnelBlastedScore;
					PlayerOneScore += TunnelBlastedScore;

					NitroglycerineHit[0] = 0;
					NitroglycerineHit[1] = 0;
				}
				else if (NitroglycerineHit[0] == 1 || NitroglycerineHit[1] == 1)
				{
					PlayerOneScore += TunnelBlastedScore;
					PlayerOneScore += TunnelBlastedScore;

					NitroglycerineHit[0] = 0;
					NitroglycerineHit[1] = 0;
				}
				else
				{
					PlayerOneScore += TunnelBlastedScore;
				}

				if (MutuallyExclusiveModeActive == 0)
				{
					DataToSend[15] = 6; //Tunnel
					DataToSend[16] = 2; //A Tunnel has been blasted
				}
			}
			else
			{
				PlayerOneScore += PartialTunnelScore;
			}
		}
		if (VUKTriggered[3] == 1 && CurrentProgramMillis > VUKTriggeredMillis[3] + 1000 && MutuallyExclusiveModeActive == 1)
		{
			CoilFireFlags[2][3] = 1;
			VUKTriggered[3] = 0;

			VUKTriggeredMillis[3] = CurrentProgramMillis;
		}
		if (VUKTriggered[3] == 1 && CurrentProgramMillis > VUKTriggeredMillis[3] + 3000 && MutuallyExclusiveModeActive == 0)
		{
			if (ChosenRailroad == 1 && TunnelMessageStatus == 0) //CPRR
			{
				if (NumberOfTunnelsBlasted >= CPRRTunnelsBlasted)
				{
					DataToSend[15] = 0;
				}
				else if (PartialTunnelsCompleted != 0)
				{
					DataToSend[15] = 0;
				}
				else
				{
					DataToSend[15] = 6; //Tunnel
					DataToSend[16] = 3; //You need X more tunnels
					DataToSend[17] = CPRRTunnelsBlasted - NumberOfTunnelsBlasted; 
					TunnelMessageStatus = 1;
				}
			}
			else if (ChosenRailroad == 2 && TunnelMessageStatus == 0) //UPRR
			{
				if (NumberOfTunnelsBlasted >= UPRRTunnelsBlasted)
				{
					DataToSend[15] = 0;
				}
				else if (PartialTunnelsCompleted != 0)
				{
					DataToSend[15] = 0;
				}
				else
				{
					DataToSend[15] = 6; //Tunnel
					DataToSend[16] = 3; //You need X more tunnels
					DataToSend[17] = UPRRTunnelsBlasted - NumberOfTunnelsBlasted;
					TunnelMessageStatus = 1;
				}
			}

			//Delay coil firing if second message shown
			if (DataToSend[16] == 3 && TunnelMessageStatus == 1)
			{
				VUKTriggeredMillis[3] = CurrentProgramMillis;
				TunnelMessageStatus = 2;
			}
			else
			{
				TunnelMessageStatus = 0;
				
				DataToSend[15] = 0;

				CoilFireFlags[2][3] = 1;
				VUKTriggered[3] = 0;

				VUKTriggeredMillis[3] = CurrentProgramMillis;
			}
		}

		//Handles railroad completion goals
		if (ChosenRailroad == 1) //CPRR
		{
			if (NumberOfTrestlesBuilt >= CPRRTrestlesBuilt)
			{
				LowerLEDColor[16] = 8;
				LowerLEDState[16] = 1;
			}
			else
			{
				LowerLEDState[16] = 0;
			}

			if (NumberOfTunnelsBlasted >= CPRRTunnelsBlasted)
			{
				LowerLEDColor[17] = 8;
				LowerLEDState[17] = 1;
			}
			else
			{
				LowerLEDState[17] = 0;
			}

			if (MilesOfTrackLaid >= CPRRMilesOfTrack)
			{
				LowerLEDColor[15] = 8;
				LowerLEDState[15] = 1;
			}
			else
			{
				LowerLEDState[15] = 0;
			}

			if (NumberOfIndianSquabbles >= CPRRIndianSquabbles && NumberOfEnginesAcquired >= CPRRLocomotivesAcquired && TrackFrenziesCompleted >= CPRRTrackFrenzies && MilesOfTrackLaid >= CPRRMilesOfTrack && NumberOfTunnelsBlasted >= CPRRTunnelsBlasted && NumberOfTrestlesBuilt >= CPRRTrestlesBuilt) //Have all the missions been completed?
			{
				//The Central Pacific Railroad has been built.
				RailroadCompleted = 1;
			}
		}
		if (ChosenRailroad == 2) //UPRR
		{
			if (NumberOfTrestlesBuilt >= UPRRTrestlesBuilt)
			{
				LowerLEDColor[16] = 8;
				LowerLEDState[16] = 1;
			}
			else
			{
				LowerLEDState[16] = 0;
			}

			if (NumberOfTunnelsBlasted >= UPRRTunnelsBlasted)
			{
				LowerLEDColor[17] = 8;
				LowerLEDState[17] = 1;
			}
			else
			{
				LowerLEDState[17] = 0;
			}

			if (MilesOfTrackLaid >= UPRRMilesOfTrack)
			{
				LowerLEDColor[15] = 8;
				LowerLEDState[15] = 1;
			}
			else
			{
				LowerLEDState[15] = 0;
			}

			if (NumberOfIndianSquabbles >= UPRRIndianSquabbles && NumberOfEnginesAcquired >= UPRRLocomotivesAcquired && TrackFrenziesCompleted >= UPRRTrackFrenzies && MilesOfTrackLaid >= UPRRMilesOfTrack && NumberOfTunnelsBlasted >= UPRRTunnelsBlasted && NumberOfTrestlesBuilt >= UPRRTrestlesBuilt) //Have all the missions been completed?
			{
				//The Union Pacific Railroad has been built.
				RailroadCompleted = 1;
			}
		}

		if (BallsInTroughLogicDisabledByExecutiveOrder == 0 && NumBallsInTrough > NumBallsInstalled - NumBallsShouldBeInPlay)
		{
			if (MultiballModeActive == 1) //Has a ball been drained during the INDIANS mode?
			{
				if (INDIANSModeActive == 1 && CurrentProgramMillis < ShootAgainMillis + INDIANSMultiballGracePeriod + 2000)
				{
					CoilFireFlags[0][5] = 1; //Eject a ball
					BallCounterDisableMillis = CurrentProgramMillis;
					NumBallsInTrough--;

					return;
				}
				else if (TrackFrenzyModeActive == 1 && CurrentProgramMillis < ShootAgainMillis + TrackFrenzyMultiballGracePeriod + 2000)
				{
					CoilFireFlags[0][5] = 1; //Eject a ball
					BallCounterDisableMillis = CurrentProgramMillis;
					NumBallsInTrough--;

					return;
				}
				else if (RailroadCompletedMultiballModeActive == 1 && CurrentProgramMillis < ShootAgainMillis + RailroadCompletedMultiballGracePeriod + 2000)
				{
					CoilFireFlags[0][5] = 1; //Eject a ball
					BallCounterDisableMillis = CurrentProgramMillis;
					NumBallsInTrough--;

					return;
				}

			}
			if (INDIANSModeActive == 1 && CurrentProgramMillis > INDIANSMultiballGracePeriod + ShootAgainGracePeriod + 2000) //Has a ball been drained outside of the INDIANS mode grace period?
			{
				INDIANSModeActive = 0;
				if (INDIANSGoalCompleted == 1)
				{
					NumberOfIndianSquabbles++;

					INDIANSModeMessageState = 5;
					INDIANSModeMessageMillis = CurrentProgramMillis;

					DataToSend[15] = 9;
					DataToSend[16] = 5;
				}
				else
				{
					INDIANSModeMessageState = 6;
					INDIANSModeMessageMillis = CurrentProgramMillis;

					DataToSend[15] = 9;
					DataToSend[16] = 6;
				}
				INDIANSGoalCompleted = 0;
				RAILCompleted = 0;
				NumBallsShouldBeInPlay = 1;

				MutuallyExclusiveModeActive = 0;
				MultiballModeActive = 0;

				DropTargetNeedsToBeReset = 1;

				return;
			}
			if (TrackFrenzyModeActive == 1 && CurrentProgramMillis > ShootAgainMillis + TrackFrenzyMultiballGracePeriod + 2000) //Has a ball been drained outside of the track frenzy mode grace period?
			{
				TrackFrenzyModeActive = 0;
				if (MilesOfTrackBuiltDuringFrenzy >= TrackFrenzyMilesGoal)
				{
					TrackFrenziesCompleted++;
				}
				MilesOfTrackBuiltDuringFrenzy = 0;

				NumBallsShouldBeInPlay = 1;

				MutuallyExclusiveModeActive = 0;
				MultiballModeActive = 0;

				DataToSend[15] = 0;

				return;
			}
			if (RailroadCompletedMultiballModeActive == 1 && CurrentProgramMillis > ShootAgainMillis + RailroadCompletedMultiballGracePeriod + 2000) //Has a ball been drained outside of the railroad completed multiball mode grace period?
			{
				NumBallsShouldBeInPlay--;

				if (NumBallsShouldBeInPlay == 1)
				{
					MutuallyExclusiveModeActive = 0;
					MultiballModeActive = 0;

					RailroadCompletedMultiballModeActive = 0;
				}

				return;
			}
		}
		if(BallsInTroughLogicDisabledByExecutiveOrder == 0 && NumBallsInTrough == NumBallsInstalled)
		{
			if (TroughJammed == 1)
			{
				TroughJammed = 0;
				Serial.println(F("The jam has been cleared."));
				WriteToCoil(22, 1); //Re-enable the flippers
				CoilFireFlags[0][5] = 1; //Eject a ball so the player can continue their game
				NumBallsInTrough--;
				BallCounterDisableMillis = CurrentProgramMillis; //Disable the ball counter
				return; //B/c we don't want the game to think a player drained a ball when the game was just doing what it had to to clear a jam.
			}

			if (CurrentProgramMillis < ShootAgainMillis + ShootAgainGracePeriod + 2000)
			{
				CoilFireFlags[0][5] = 1; //Eject a ball
				BallCounterDisableMillis = CurrentProgramMillis;
				NumBallsInTrough--;

				BallJustSaved = 1;

				if (MutuallyExclusiveModeActive == 0)
				{
					DataToSend[15] = 10;
					BallSavedState = 1;
					BallSavedMillis = CurrentProgramMillis;
				}

				return;
			}

			CurrentBallNum++;
			if (CurrentBallNum > NumBallsPerGame + ExtraBallsEarned) //Basically, has the player run out of balls?
			{
				WriteToCoil(22, 0); //Disable the flippers

				EndOfBallBonusStage = 1;
				StageHappenedYet = 0;
				EndOfBallBonusMillis = CurrentProgramMillis;

				BallsInTroughLogicDisabledByExecutiveOrder = 1;
			}
			else
			{
				EndOfBallBonusStage = 1;
				StageHappenedYet = 0;
				EndOfBallBonusMillis = CurrentProgramMillis;

				DataToSend[15] = 2;
				DataToSend[16] = 1;

				BallsInTroughLogicDisabledByExecutiveOrder = 1;
			}
		}

		//Match
		if (MatchState == 1 && CurrentProgramMillis > MatchMillis + 1500) //Show the # to match
		{
			MatchState = 2;
			MatchMillis = CurrentProgramMillis;

			DataToSend[16] = MatchState;
		}
		if (MatchState == 2 && CurrentProgramMillis > MatchMillis + 500) //Begin to loop through random numbers
		{
			MatchState = 3;
			MatchMillis = CurrentProgramMillis;

			DataToSend[16] = MatchState;
		}
		if (MatchState == 3 && CurrentProgramMillis > MatchMillis + 4500) //Show # yielded
		{
			MatchState = 4;
			MatchMillis = CurrentProgramMillis;

			DataToSend[16] = MatchState;
			RandomMatchNumber = random(0, 10) * 10;
			RandomMatchNumber += DataToSend[11]; //Make sure the last digit of the match always matches the player's score; keep odds ~1/10
			DataToSend[18] = RandomMatchNumber;
		}
		if (MatchState == 4 && CurrentProgramMillis > MatchMillis + 1500) //If win or lose
		{
			if (DataToSend[17] == RandomMatchNumber) //Horay! You won the match!
			{
				DataToSend[19] = TypeOfMatchReward;
			}
			else
			{
				DataToSend[19] = 2;
			}
			MatchState = 5;
			MatchMillis = CurrentProgramMillis;
			DataToSend[16] = MatchState;
		}
		if (MatchState == 5 && CurrentProgramMillis > MatchMillis + 3000)
		{
			MatchState = 0;
			if (DataToSend[17] == RandomMatchNumber)
			{
				if (TypeOfMatchReward == 0) //Extra ball
				{
					ExtraBallsEarned++;

					WriteToCoil(22, 1); //re-enable the flippers

					BallEndState = 1;
					BallEndMillis = CurrentProgramMillis;
				}
				else if (TypeOfMatchReward == 1) //Free game
				{
					NumCoinsInserted += PricePerGame;
				}
				DataToSend[15] = 0;
			}
			if ((DataToSend[17] == RandomMatchNumber && TypeOfMatchReward == 1) || DataToSend[17] != RandomMatchNumber)
			{
				//Game Over
				if (CheckForHighScore() != 0)
				{
					//Do the High Score stuff
					HighScoreMessageState = 1;
					HighScoreMessageMillis = CurrentProgramMillis;

					WriteToCoil(22, 0); //disable flippers

					DataToSend[15] = 13;
					DataToSend[16] = HighScoreMessageState;
					DataToSend[17] = HighScoreRank;
				}
				else
				{
					GameOverLogic();
				}
			}
		}

		//High Score Wrangling
		if (HighScoreMessageState == 1 && CurrentProgramMillis > HighScoreMessageMillis + 4000) //SHOW: Player 1 Achieved High Score #   for how long
		{
			CurrentlySelectedHighScoreLetter = 77;
			CharacterSelectionPosition = 0;

			HighScoreMessageState = 2;
			DataToSend[16] = HighScoreMessageState;
			HighScoreMessageMillis = CurrentProgramMillis;
		}
		if (HighScoreMessageState == 2 && CurrentProgramMillis > HighScoreMessageMillis + 3000) //SHOW: Enter your initials   for how long
		{
			HighScoreMessageState = 3;
			DataToSend[16] = HighScoreMessageState;
			HighScoreMessageMillis = CurrentProgramMillis;
		}
		if (HighScoreMessageState == 3 && CurrentProgramMillis > HighScoreMessageMillis + 3000) //SHOW: Flippers scroll, start chooses   for how long
		{
			HighScoreMessageState = 4;
			DataToSend[16] = HighScoreMessageState;
			HighScoreMessageMillis = CurrentProgramMillis;
		}
		if (HighScoreMessageState == 4) //Do the actual entering of initials
		{
			if (CurrentProgramMillis > HighScoreMessageMillis + TimeGivenToEnterInitials) //Enter initials timeout
			{
				HighScoreMessageState = 5;
				DataToSend[16] = HighScoreMessageState;
				HighScoreMessageMillis = CurrentProgramMillis;
			}

			MathVariable = HighScoreMessageMillis + TimeGivenToEnterInitials - CurrentProgramMillis;
			MathVariableTwo = 0;
			while (MathVariable >= 1000)
			{
				MathVariable -= 1000;
				MathVariableTwo++;
			}
			DataToSend[17] = MathVariableTwo;

			DataToSend[20] = 0;

			if (CurrentSwitchState[8][1] == 0 && CurrentSwitchState[8][5] == 0)
			{
				HoldingFlipperButtonMillis = CurrentProgramMillis;
			}
			//List of characters allowed
			//ASCII 32 - 47,
			//ASCII 58 - 63
			//ASCII 65 - 96,
			//ASCII 123 - 127
			//ASCII 127 (DELETE) is used as BACKSPACE
			if (HitSwitches[8][1] == 1 || (CurrentSwitchState[8][1] == 1 && CurrentProgramMillis > HoldingFlipperButtonMillis + 500 && CurrentProgramMillis > LastAutoScrollMillis + 100)) //Left flipper
			{
				CurrentlySelectedHighScoreLetter--;

				if (CurrentProgramMillis > HoldingFlipperButtonMillis + 500 && CurrentProgramMillis > LastAutoScrollMillis + 100)
				{
					LastAutoScrollMillis = CurrentProgramMillis;
				}

				if (CurrentlySelectedHighScoreLetter == 31) //Avoids ASCII characters that are not on the list
				{
					CurrentlySelectedHighScoreLetter = 127;
				}
				if (CurrentlySelectedHighScoreLetter == 57)
				{
					CurrentlySelectedHighScoreLetter = 47;
				}
				if (CurrentlySelectedHighScoreLetter == 64)
				{
					CurrentlySelectedHighScoreLetter = 63;
				}
				if (CurrentlySelectedHighScoreLetter == 122)
				{
					CurrentlySelectedHighScoreLetter = 96;
				}
			}
			if (HitSwitches[8][5] == 1 || (CurrentSwitchState[8][5] == 1 && CurrentProgramMillis > HoldingFlipperButtonMillis + 500 && CurrentProgramMillis > LastAutoScrollMillis + 100)) //Right flipper
			{
				CurrentlySelectedHighScoreLetter++;

				if (CurrentProgramMillis > HoldingFlipperButtonMillis + 500 && CurrentProgramMillis > LastAutoScrollMillis + 100)
				{
					LastAutoScrollMillis = CurrentProgramMillis;
				}

				if (CurrentlySelectedHighScoreLetter == 48) //Avoids ASCII characters that are not on the list
				{
					CurrentlySelectedHighScoreLetter = 58;
				}
				if (CurrentlySelectedHighScoreLetter == 64)
				{
					CurrentlySelectedHighScoreLetter = 65;
				}
				if (CurrentlySelectedHighScoreLetter == 97)
				{
					CurrentlySelectedHighScoreLetter = 123;
				}
				if (CurrentlySelectedHighScoreLetter == 128)
				{
					CurrentlySelectedHighScoreLetter = 32;
				}
			}

			DataToSend[18] = CharacterSelectionPosition;
			DataToSend[19] = CurrentlySelectedHighScoreLetter;

			if (DataToSend[18] > 4) //All characters/initials entered
			{
				//Show reward, score entry, etc.
				HighScoreMessageState = 5;
				DataToSend[16] = HighScoreMessageState;
				DataToSend[17] = FreeGameOnHighScore;
				HighScoreMessageMillis = CurrentProgramMillis;

				if (FreeGameOnHighScore == 1)
				{
					NumCoinsInserted += PricePerGame;
				}

				//Save the High Scores
				if (SaveHighScores == 1)
				{
					SD.remove("RECORDS.txt"); //There doesn't seem to be a way to replace bytes in the file, so delete and make a new one.
					RECORDSFile = SD.open("RECORDS.txt", FILE_WRITE);
					for (byte x = 0; x < 9; x++) //High Score #1 is top line, #9 is bottom
					{
						for (byte y = 0; y < 15; y++)
						{
							if (y > 4) //Score part
							{
								RECORDSFile.write(HighScoreArray[x][y] + 48); //Convert binary 0-9 to ASCII 0-9 (48-57)
							}
							else //Character part
							{
								RECORDSFile.write(HighScoreArray[x][y]);
							}

							if (y == 14)
							{
								RECORDSFile.println("");
							}
						}
					}
					RECORDSFile.close();
				}
			}

			if (HitSwitches[8][2] == 1) //Start button
			{
				if (CurrentlySelectedHighScoreLetter == ASCII_DELETE) //backspace
				{
					if (CharacterSelectionPosition > 0)
					{
						CharacterSelectionPosition--;
					}
				}
				else
				{
					HighScoreArray[HighScoreRank - 1][CharacterSelectionPosition] = CurrentlySelectedHighScoreLetter;
					DataToSend[20] = CurrentlySelectedHighScoreLetter;
					CharacterSelectionPosition++;
				}
			}

		}
		if (HighScoreMessageState == 5 && CurrentProgramMillis > HighScoreMessageMillis + 10000)
		{
			HighScoreMessageState = 0;
			GameOverLogic();
		}

		//End-of-ball bonus
		if (EndOfBallBonusStage == 1 && CurrentProgramMillis > EndOfBallBonusMillis + 2500)
		{
			EndOfBallBonusMillis = CurrentProgramMillis;
			EndOfBallBonusStage++;
			StageHappenedYet = 0;
		}
		else if (EndOfBallBonusStage == 4 && CurrentProgramMillis > EndOfBallBonusMillis + 4000)
		{
			EndOfBallBonusMillis = CurrentProgramMillis;
			EndOfBallBonusStage++;
			StageHappenedYet = 0;
		}
		else if (EndOfBallBonusStage == 10 && CurrentProgramMillis > EndOfBallBonusMillis + 3500)
		{
			EndOfBallBonusMillis = CurrentProgramMillis;
			EndOfBallBonusStage++;
			StageHappenedYet = 0;
		}
		else if (EndOfBallBonusStage != 0 && EndOfBallBonusStage != 1 && EndOfBallBonusStage != 10 && EndOfBallBonusStage != 4 && CurrentProgramMillis > EndOfBallBonusMillis + 3500)
		{
			EndOfBallBonusMillis = CurrentProgramMillis;
			EndOfBallBonusStage++;
			StageHappenedYet = 0;
		}

		if (EndOfBallBonusStage == 1 && StageHappenedYet == 0) //"BONUS" Screen
		{
			StageHappenedYet = 1;
			
			WriteToCoil(22, 0); //disable flippers

			DiverterOpen = 0;

			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;
		}
		if (EndOfBallBonusStage == 2 && StageHappenedYet == 0) //Track
		{
			StageHappenedYet = 1;
			
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			if (MilesOfTrackLaid > 65535) //16 bits
			{
				DataToSend[17] = 255; //8 MSBs
				DataToSend[18] = 255; //8 LSBs

				PlayerOneScore += 65535 * BonusPerMileOfTrack;
			}
			else
			{
				MathVariable = MilesOfTrackLaid;
				MathVariableTwo = 0;
				while (MathVariable >= 256)
				{
					MathVariable -= 256;
					MathVariableTwo++;
				}
				DataToSend[17] = MathVariableTwo; //MSBs
				DataToSend[18] = MathVariable; //LSBs

				PlayerOneScore += MilesOfTrackLaid * BonusPerMileOfTrack;
			}

			if (BonusPerMileOfTrack > 65535) //Who'd ever do that anyway, though?
			{
				DataToSend[19] = 255;
				DataToSend[20] = 255;
			}
			else
			{
				MathVariable = BonusPerMileOfTrack;
				MathVariableTwo = 0;
				while (MathVariable > 255)
				{
					MathVariable -= 256;
					MathVariableTwo++;
				}
				DataToSend[19] = MathVariableTwo;
				DataToSend[20] = MathVariable;
			}

			if (MilesOfTrackLaid == 0)
			{
				EndOfBallBonusStage = 3;
			}
		}
		if (EndOfBallBonusStage == 3 && StageHappenedYet == 0) //The date is...
		{
			StageHappenedYet = 1;
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			DataToSend[18] = ChosenRailroad;
		}
		if (EndOfBallBonusStage == 4 && StageHappenedYet == 0) //The tracks have reached...
		{
			StageHappenedYet = 1;
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			DataToSend[18] = ChosenRailroad;
		}
		if (EndOfBallBonusStage == 5 && StageHappenedYet == 0) //Track Frenzy
		{
			StageHappenedYet = 1;
			
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			if (TrackFrenziesCompleted > 255)
			{
				DataToSend[17] = 255;
				PlayerOneScore += 255 * BonusPerTrackFrenzy;
			}
			else
			{
				DataToSend[17] = TrackFrenziesCompleted;
				PlayerOneScore += TrackFrenziesCompleted * BonusPerTrackFrenzy;
			}

			if (BonusPerTrackFrenzy > 65535) //Who'd ever do that anyway, though?
			{
				DataToSend[19] = 255;
				DataToSend[20] = 255;
			}
			else
			{
				MathVariable = BonusPerTrackFrenzy;
				MathVariableTwo = 0;
				while (MathVariable > 255)
				{
					MathVariable -= 256;
					MathVariableTwo++;
				}
				DataToSend[19] = MathVariableTwo;
				DataToSend[20] = MathVariable;
			}

			if (TrackFrenziesCompleted == 0)
			{
				EndOfBallBonusStage = 6;
				StageHappenedYet = 0;
			}
		}
		if (EndOfBallBonusStage == 6 && StageHappenedYet == 0) //Trestle
		{
			StageHappenedYet = 1;
			
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			if (NumberOfTrestlesBuilt > 255)
			{
				DataToSend[17] = 255;
				PlayerOneScore += 255 * BonusPerTrestleBuilt;
			}
			else
			{
				DataToSend[17] = NumberOfTrestlesBuilt;
				PlayerOneScore += NumberOfTrestlesBuilt * BonusPerTrestleBuilt;
			}

			if (BonusPerTrestleBuilt > 65535) //Who'd ever do that anyway, though?
			{
				DataToSend[19] = 255;
				DataToSend[20] = 255;
			}
			else
			{
				MathVariable = BonusPerTrestleBuilt;
				MathVariableTwo = 0;
				while (MathVariable > 255)
				{
					MathVariable -= 256;
					MathVariableTwo++;
				}
				DataToSend[19] = MathVariableTwo;
				DataToSend[20] = MathVariable;
			}

			if (NumberOfTrestlesBuilt == 0)
			{
				EndOfBallBonusStage = 7;
				StageHappenedYet = 0;
			}
		}
		if (EndOfBallBonusStage == 7 && StageHappenedYet == 0) //Tunnel
		{
			StageHappenedYet = 1;
			
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			if (NumberOfTunnelsBlasted > 255)
			{
				DataToSend[17] = 255;
				PlayerOneScore += 255 * BonusPerTunnelBlasted;
			}
			else
			{
				DataToSend[17] = NumberOfTunnelsBlasted;
				PlayerOneScore += NumberOfTunnelsBlasted * BonusPerTunnelBlasted;
			}

			if (BonusPerTunnelBlasted > 65535) //Who'd ever do that anyway, though?
			{
				DataToSend[19] = 255;
				DataToSend[20] = 255;
			}
			else
			{
				MathVariable = BonusPerTunnelBlasted;
				MathVariableTwo = 0;
				while (MathVariable > 255)
				{
					MathVariable -= 256;
					MathVariableTwo++;
				}
				DataToSend[19] = MathVariableTwo;
				DataToSend[20] = MathVariable;
			}

			if (NumberOfTunnelsBlasted == 0)
			{
				EndOfBallBonusStage = 8;
				StageHappenedYet = 0;
			}
		}
		if (EndOfBallBonusStage == 8 && StageHappenedYet == 0) //Locomotive
		{
			StageHappenedYet = 1;
			
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			if (NumberOfEnginesAcquired > 255)
			{
				DataToSend[17] = 255;
				PlayerOneScore += 255 * BonusPerLocomotiveAcquired;
			}
			else
			{
				DataToSend[17] = NumberOfEnginesAcquired;
				PlayerOneScore += NumberOfEnginesAcquired * BonusPerLocomotiveAcquired;
			}

			if (BonusPerLocomotiveAcquired > 65535) //Who'd ever do that anyway, though?
			{
				DataToSend[19] = 255;
				DataToSend[20] = 255;
			}
			else
			{
				MathVariable = BonusPerLocomotiveAcquired;
				MathVariableTwo = 0;
				while (MathVariable > 255)
				{
					MathVariable -= 256;
					MathVariableTwo++;
				}
				DataToSend[19] = MathVariableTwo;
				DataToSend[20] = MathVariable;
			}

			if (NumberOfEnginesAcquired == 0)
			{
				EndOfBallBonusStage = 9;
				StageHappenedYet = 0;
			}
		}
		if (EndOfBallBonusStage == 9 && StageHappenedYet == 0) //INDIAN Squabble
		{
			StageHappenedYet = 1;
			
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			if (NumberOfIndianSquabbles > 255)
			{
				DataToSend[17] = 255;
				PlayerOneScore += 255 * BonusPerIndianSquabble;
			}
			else
			{
				DataToSend[17] = NumberOfIndianSquabbles;
				PlayerOneScore += NumberOfIndianSquabbles * BonusPerIndianSquabble;
			}

			if (BonusPerIndianSquabble > 65535) //Who'd ever do that anyway, though?
			{
				DataToSend[19] = 255;
				DataToSend[20] = 255;
			}
			else
			{
				MathVariable = BonusPerIndianSquabble;
				MathVariableTwo = 0;
				while (MathVariable > 255)
				{
					MathVariable -= 256;
					MathVariableTwo++;
				}
				DataToSend[19] = MathVariableTwo;
				DataToSend[20] = MathVariable;
			}

			if (NumberOfIndianSquabbles == 0)
			{
				EndOfBallBonusStage = 10;
				StageHappenedYet = 0;
			}
		}
		if (EndOfBallBonusStage == 10 && StageHappenedYet == 0) //Total bonus
		{
			StageHappenedYet = 1;
			
			DataToSend[15] = 2; //2 is EoB bonus
			DataToSend[16] = EndOfBallBonusStage;

			//The DMD will calculate and display the total bonus based on all the previous information sent, not much to do here.
		}
		if (EndOfBallBonusStage == 11 && StageHappenedYet == 0) //EoB bonus is over, proceed normally
		{
			if (CurrentBallNum > NumBallsPerGame + ExtraBallsEarned)
			{
				if (MatchHappenedYetThisGame == 0)
				{
					EndOfBallBonusStage = 0;
					
					MatchHappenedYetThisGame = 1;
					MatchState = 1;
					MatchMillis = CurrentProgramMillis;

					DataToSend[15] = 12;
					DataToSend[16] = 1; //Show the big MATCH thing

					DataToSend[17] = DataToSend[10] * 10;
					DataToSend[17] += DataToSend[11];
				}
				else
				{
					EndOfBallBonusStage = 0;

					//Game over, check for high score
					if (CheckForHighScore() != 0)
					{
						//Do High Score Stuff
						HighScoreMessageState = 1;
						HighScoreMessageMillis = CurrentProgramMillis;

						WriteToCoil(22, 0); //disable flippers

						DataToSend[15] = 13;
						DataToSend[16] = HighScoreMessageState;
						DataToSend[17] = HighScoreRank;
					}
					else
					{
						GameOverLogic();
					}
				}
			}
			else
			{
				//Next ball
				WriteToCoil(22, 1); //re-enable the flippers

				EndOfBallBonusStage = 0;

				BallEndState = 1;
				BallEndMillis = CurrentProgramMillis;
			}
		}
		//Stuff that happens AFTER the end-of-ball bonus.
		if (BallEndState == 1 && CurrentProgramMillis > BallEndMillis + 500)
		{
			if (ResetDropTargetBetweenBalls == 1 && (CurrentSwitchState[3][0] == 1 || CurrentSwitchState[3][1] == 1 || CurrentSwitchState[3][2] == 1 || CurrentSwitchState[4][2] == 1))
			{
				CoilFireFlags[2][7] = 1;
				DropTargetBeingDebounced = 1;
			}

			BallEndState = 2;
			BallEndMillis = CurrentProgramMillis;
		}
		if (BallEndState == 2 && CurrentProgramMillis > BallEndMillis + 500)
		{
			if (ResetDropTargetBetweenBalls == 1)
			{
				DropTargetBeingDebounced = 0;
				RAILTriggered[0] = 0;
				RAILTriggered[1] = 0;
				RAILTriggered[2] = 0;
				RAILTriggered[3] = 0;

				UpperLEDState[22] = 0;
				UpperLEDState[23] = 0;
				UpperLEDState[24] = 0;
				UpperLEDState[25] = 0;

				RAILCompleted = 0;

				UpperLEDState[17] = 0;
			}
			
			BallEndState = 0;

			CoilFireFlags[0][5] = 1; //Eject a ball
			BallCounterDisableMillis = CurrentProgramMillis;
			NumBallsInTrough--;
			Serial.print(F("The Current Ball is "));
			Serial.print(CurrentBallNum);
			Serial.println(F("."));

			ShootAgainMillis = CurrentProgramMillis;
			ShootAgainActive = 1;

			BallsInTroughLogicDisabledByExecutiveOrder = 0;

			DataToSend[15] = 0; //Normal screen
			DataToSend[16] = 0;
		}
	}
}

void GameOverLogic()
{
	//Game Over
	GameActive = 0;
	Serial.println(F("Game Over"));

	WriteToCoil(22, 0); //disable flippers
	WriteToCoil(7, 0); //Turn off the start button light

	//Reset all of the game variables at the end of the game.
	CurrentBallNum = 0;
	ExtraBallsEarned = 0;
	NumBallsShouldBeInPlay = 0;

	STEAMCompleted = 0;
	TCRRCompleted = 0;
	ENGINECompleted = 0;
	INDIANSCompleted = 0;

	DropTargetNeedsToBeReset = 0;

	NumBallsInTrough = HowManyBalls();

	DiverterOpen = 0;

	EndOfBallBonusStage = 0;

	LastExtraBallScore = 0;

	DataToSend[15] = 0; //Normal screen
	DataToSend[16] = 0;
}

byte CheckForHighScore()
{
	//Assemble the High Scores
	for (byte x = 0; x < 9; x++)
	{
		AssembledHighScores[x] = HighScoreArray[x][5] * 1000000000;
		AssembledHighScores[x] += HighScoreArray[x][6] * 100000000;
		AssembledHighScores[x] += HighScoreArray[x][7] * 10000000;
		AssembledHighScores[x] += HighScoreArray[x][8] * 1000000;
		AssembledHighScores[x] += HighScoreArray[x][9] * 100000;
		AssembledHighScores[x] += HighScoreArray[x][10] * 10000;
		AssembledHighScores[x] += HighScoreArray[x][11] * 1000;
		AssembledHighScores[x] += HighScoreArray[x][12] * 100;
		AssembledHighScores[x] += HighScoreArray[x][13] * 10;
		AssembledHighScores[x] += HighScoreArray[x][14];
	}

	//Check if greater than an Assembled High Score
	if (PlayerOneScore > AssembledHighScores[0]) //Beat HS one
	{
		HighScoreRank = 1;
	}
	else if (PlayerOneScore > AssembledHighScores[1]) //Beat HS two
	{
		HighScoreRank = 2;
	}
	else if (PlayerOneScore > AssembledHighScores[2]) //Beat HS three
	{
		HighScoreRank = 3;
	}
	else if (PlayerOneScore > AssembledHighScores[3]) //Beat HS four
	{
		HighScoreRank = 4;
	}
	else if (PlayerOneScore > AssembledHighScores[4]) //Beat HS five
	{
		HighScoreRank = 5;
	}
	else if (PlayerOneScore > AssembledHighScores[5]) //Beat HS six
	{
		HighScoreRank = 6;
	}
	else if (PlayerOneScore > AssembledHighScores[6]) //Beat HS seven
	{
		HighScoreRank = 7;
	}
	else if (PlayerOneScore > AssembledHighScores[7]) //Beat HS eight
	{
		HighScoreRank = 8;
	}
	else if (PlayerOneScore > AssembledHighScores[8]) //Beat HS nine
	{
		HighScoreRank = 9;
	}
	else //Did not beat any of the high scores
	{
		HighScoreRank = 0;
	}

	if (HighScoreRank <= 8)
	{
		for (byte x = 0; x < 15; x++)
		{
			HighScoreArray[8][x] = HighScoreArray[7][x];
		}
	}
	if (HighScoreRank <= 7)
	{
		for (byte x = 0; x < 15; x++)
		{
			HighScoreArray[7][x] = HighScoreArray[6][x];
		}
	}
	if (HighScoreRank <= 6)
	{
		for (byte x = 0; x < 15; x++)
		{
			HighScoreArray[6][x] = HighScoreArray[5][x];
		}
	}
	if (HighScoreRank <= 5)
	{
		for (byte x = 0; x < 15; x++)
		{
			HighScoreArray[5][x] = HighScoreArray[4][x];
		}
	}
	if (HighScoreRank <= 4)
	{
		for (byte x = 0; x < 15; x++)
		{
			HighScoreArray[4][x] = HighScoreArray[3][x];
		}
	}
	if (HighScoreRank <= 3)
	{
		for (byte x = 0; x < 15; x++)
		{
			HighScoreArray[3][x] = HighScoreArray[2][x];
		}
	}
	if (HighScoreRank <= 2)
	{
		for (byte x = 0; x < 15; x++)
		{
			HighScoreArray[2][x] = HighScoreArray[1][x];
		}
	}
	if (HighScoreRank == 1)
	{
		for (byte x = 0; x < 15; x++)
		{
			HighScoreArray[1][x] = HighScoreArray[0][x];
		}
	}

	if (HighScoreRank != 0)
	{
		HighScoreArray[HighScoreRank - 1][5] = DataToSend[2];
		HighScoreArray[HighScoreRank - 1][6] = DataToSend[3];
		HighScoreArray[HighScoreRank - 1][7] = DataToSend[4];
		HighScoreArray[HighScoreRank - 1][8] = DataToSend[5];
		HighScoreArray[HighScoreRank - 1][9] = DataToSend[6];
		HighScoreArray[HighScoreRank - 1][10] = DataToSend[7];
		HighScoreArray[HighScoreRank - 1][11] = DataToSend[8];
		HighScoreArray[HighScoreRank - 1][12] = DataToSend[9];
		HighScoreArray[HighScoreRank - 1][13] = DataToSend[10];
		HighScoreArray[HighScoreRank - 1][14] = DataToSend[11];
	}

	return HighScoreRank;
}

byte HowManyBalls()
{
	//Counts the number of balls in the trough.
	byte BallCount = 0;

	if (CurrentSwitchState[7][2] == 1)
	{
		BallCount++;
	}
	else
	{
		return BallCount;
	}

	if (CurrentSwitchState[6][6] == 1)
	{
		BallCount++;
	}
	else
	{
		return BallCount;
	}

	if (CurrentSwitchState[6][5] == 1)
	{
		BallCount++;
	}
	else
	{
		return BallCount;
	}

	if (CurrentSwitchState[6][4] == 1)
	{
		BallCount++;
	}
	else
	{
		return BallCount;
	}

	if (CurrentSwitchState[6][3] == 1)
	{
		BallCount++;
	}
	else
	{
		return BallCount;
	}

	if (CurrentSwitchState[6][2] == 1)
	{
		BallCount++;
	}
	else
	{
		return BallCount;
	}

	if (CurrentSwitchState[6][1] == 1)
	{
		BallCount++;
	}
	else
	{
		return BallCount;
	}

	if (CurrentSwitchState[6][0] == 1)
	{
		BallCount++;
	}
	else
	{
		return BallCount;
	}
	return BallCount;
}

void RunOnceTests()
{
	if (SelfTestPassed == 1)
	{
		return;
	}

	if (SelfTestPassed == 0)
	{
		if (SelfTestState == 0) //Results of SD initialization
		{
			SelfTestState = 1;

			if (SDWorking == 1)
			{
				if (VerboseSelfTest == 0)
				{
					SelfTestState = 2;
					HighScoreSendMillis = CurrentProgramMillis;
				}
				else
				{
					DataToSend[15] = 14;
					DataToSend[16] = 2;
					SelfTestMessageMillis = CurrentProgramMillis;
				}

				Serial.println(F("SD initialized"));
			}
			else
			{
				DataToSend[15] = 14;
				DataToSend[16] = 3;
				SelfTestMessageMillis = CurrentProgramMillis;

				Serial.println(F("SD failed to initialize"));
			}
			
		}
		if (SelfTestState == 2)
		{
			if (SDWorking == 1)
			{
				DataToSend[15] = 14;
				DataToSend[16] = 1;

				if (CurrentProgramMillis > HighScoreSendMillis + 50 && HighScoreSendState != 0)
				{
					HighScoreSendState++;
					HighScoreSendMillis = CurrentProgramMillis;
					//Serial.println(F("Scores loading"));
				}
				if (CurrentProgramMillis > HighScoreSendMillis + 250 && HighScoreSendState == 0)
				{
					HighScoreSendState++;
					HighScoreSendMillis = CurrentProgramMillis;
				}

				DataToSend[18] = HighScoreSendState;
				switch (HighScoreSendState)
				{
				case 0:
					//Nothing
					//Serial.println(F("Case 0"));
					break;
				case 1:
					//Initials
					DataToSend[2] = HighScoreArray[0][0];
					DataToSend[3] = HighScoreArray[0][1];
					DataToSend[4] = HighScoreArray[0][2];
					DataToSend[5] = HighScoreArray[0][3];
					DataToSend[6] = HighScoreArray[0][4];
					//Serial.println(F("Case 1"));
					break;
				case 2:
					//Score
					DataToSend[2] = HighScoreArray[0][5];
					DataToSend[3] = HighScoreArray[0][6];
					DataToSend[4] = HighScoreArray[0][7];
					DataToSend[5] = HighScoreArray[0][8];
					DataToSend[6] = HighScoreArray[0][9];
					DataToSend[7] = HighScoreArray[0][10];
					DataToSend[8] = HighScoreArray[0][11];
					DataToSend[9] = HighScoreArray[0][12];
					DataToSend[10] = HighScoreArray[0][13];
					DataToSend[11] = HighScoreArray[0][14];
					//Serial.println(F("Case 2"));
					break;
				case 3:
					//Initials
					DataToSend[2] = HighScoreArray[1][0];
					DataToSend[3] = HighScoreArray[1][1];
					DataToSend[4] = HighScoreArray[1][2];
					DataToSend[5] = HighScoreArray[1][3];
					DataToSend[6] = HighScoreArray[1][4];
					//Serial.println(F("Case 3"));
					break;
				case 4:
					//Score
					DataToSend[2] = HighScoreArray[1][5];
					DataToSend[3] = HighScoreArray[1][6];
					DataToSend[4] = HighScoreArray[1][7];
					DataToSend[5] = HighScoreArray[1][8];
					DataToSend[6] = HighScoreArray[1][9];
					DataToSend[7] = HighScoreArray[1][10];
					DataToSend[8] = HighScoreArray[1][11];
					DataToSend[9] = HighScoreArray[1][12];
					DataToSend[10] = HighScoreArray[1][13];
					DataToSend[11] = HighScoreArray[1][14];
					break;
				case 5:
					//Initials
					DataToSend[2] = HighScoreArray[2][0];
					DataToSend[3] = HighScoreArray[2][1];
					DataToSend[4] = HighScoreArray[2][2];
					DataToSend[5] = HighScoreArray[2][3];
					DataToSend[6] = HighScoreArray[2][4];
					break;
				case 6:
					//Score
					DataToSend[2] = HighScoreArray[2][5];
					DataToSend[3] = HighScoreArray[2][6];
					DataToSend[4] = HighScoreArray[2][7];
					DataToSend[5] = HighScoreArray[2][8];
					DataToSend[6] = HighScoreArray[2][9];
					DataToSend[7] = HighScoreArray[2][10];
					DataToSend[8] = HighScoreArray[2][11];
					DataToSend[9] = HighScoreArray[2][12];
					DataToSend[10] = HighScoreArray[2][13];
					DataToSend[11] = HighScoreArray[2][14];
					break;
				case 7:
					//Initials
					DataToSend[2] = HighScoreArray[3][0];
					DataToSend[3] = HighScoreArray[3][1];
					DataToSend[4] = HighScoreArray[3][2];
					DataToSend[5] = HighScoreArray[3][3];
					DataToSend[6] = HighScoreArray[3][4];
					break;
				case 8:
					//Score
					DataToSend[2] = HighScoreArray[3][5];
					DataToSend[3] = HighScoreArray[3][6];
					DataToSend[4] = HighScoreArray[3][7];
					DataToSend[5] = HighScoreArray[3][8];
					DataToSend[6] = HighScoreArray[3][9];
					DataToSend[7] = HighScoreArray[3][10];
					DataToSend[8] = HighScoreArray[3][11];
					DataToSend[9] = HighScoreArray[3][12];
					DataToSend[10] = HighScoreArray[3][13];
					DataToSend[11] = HighScoreArray[3][14];
					break;
				case 9:
					//Initials
					DataToSend[2] = HighScoreArray[4][0];
					DataToSend[3] = HighScoreArray[4][1];
					DataToSend[4] = HighScoreArray[4][2];
					DataToSend[5] = HighScoreArray[4][3];
					DataToSend[6] = HighScoreArray[4][4];
					break;
				case 10:
					//Score
					DataToSend[2] = HighScoreArray[4][5];
					DataToSend[3] = HighScoreArray[4][6];
					DataToSend[4] = HighScoreArray[4][7];
					DataToSend[5] = HighScoreArray[4][8];
					DataToSend[6] = HighScoreArray[4][9];
					DataToSend[7] = HighScoreArray[4][10];
					DataToSend[8] = HighScoreArray[4][11];
					DataToSend[9] = HighScoreArray[4][12];
					DataToSend[10] = HighScoreArray[4][13];
					DataToSend[11] = HighScoreArray[4][14];
					break;
				case 11:
					//Initials
					DataToSend[2] = HighScoreArray[5][0];
					DataToSend[3] = HighScoreArray[5][1];
					DataToSend[4] = HighScoreArray[5][2];
					DataToSend[5] = HighScoreArray[5][3];
					DataToSend[6] = HighScoreArray[5][4];
					break;
				case 12:
					//Score
					DataToSend[2] = HighScoreArray[5][5];
					DataToSend[3] = HighScoreArray[5][6];
					DataToSend[4] = HighScoreArray[5][7];
					DataToSend[5] = HighScoreArray[5][8];
					DataToSend[6] = HighScoreArray[5][9];
					DataToSend[7] = HighScoreArray[5][10];
					DataToSend[8] = HighScoreArray[5][11];
					DataToSend[9] = HighScoreArray[5][12];
					DataToSend[10] = HighScoreArray[5][13];
					DataToSend[11] = HighScoreArray[5][14];
					break;
				case 13:
					//Initials
					DataToSend[2] = HighScoreArray[6][0];
					DataToSend[3] = HighScoreArray[6][1];
					DataToSend[4] = HighScoreArray[6][2];
					DataToSend[5] = HighScoreArray[6][3];
					DataToSend[6] = HighScoreArray[6][4];
					break;
				case 14:
					//Score
					DataToSend[2] = HighScoreArray[6][5];
					DataToSend[3] = HighScoreArray[6][6];
					DataToSend[4] = HighScoreArray[6][7];
					DataToSend[5] = HighScoreArray[6][8];
					DataToSend[6] = HighScoreArray[6][9];
					DataToSend[7] = HighScoreArray[6][10];
					DataToSend[8] = HighScoreArray[6][11];
					DataToSend[9] = HighScoreArray[6][12];
					DataToSend[10] = HighScoreArray[6][13];
					DataToSend[11] = HighScoreArray[6][14];
					break;
				case 15:
					//Initials
					DataToSend[2] = HighScoreArray[7][0];
					DataToSend[3] = HighScoreArray[7][1];
					DataToSend[4] = HighScoreArray[7][2];
					DataToSend[5] = HighScoreArray[7][3];
					DataToSend[6] = HighScoreArray[7][4];
					break;
				case 16:
					//Score
					DataToSend[2] = HighScoreArray[7][5];
					DataToSend[3] = HighScoreArray[7][6];
					DataToSend[4] = HighScoreArray[7][7];
					DataToSend[5] = HighScoreArray[7][8];
					DataToSend[6] = HighScoreArray[7][9];
					DataToSend[7] = HighScoreArray[7][10];
					DataToSend[8] = HighScoreArray[7][11];
					DataToSend[9] = HighScoreArray[7][12];
					DataToSend[10] = HighScoreArray[7][13];
					DataToSend[11] = HighScoreArray[7][14];
					break;
				case 17:
					//Initials
					DataToSend[2] = HighScoreArray[8][0];
					DataToSend[3] = HighScoreArray[8][1];
					DataToSend[4] = HighScoreArray[8][2];
					DataToSend[5] = HighScoreArray[8][3];
					DataToSend[6] = HighScoreArray[8][4];
					break;
				case 18:
					//Score
					DataToSend[2] = HighScoreArray[8][5];
					DataToSend[3] = HighScoreArray[8][6];
					DataToSend[4] = HighScoreArray[8][7];
					DataToSend[5] = HighScoreArray[8][8];
					DataToSend[6] = HighScoreArray[8][9];
					DataToSend[7] = HighScoreArray[8][10];
					DataToSend[8] = HighScoreArray[8][11];
					DataToSend[9] = HighScoreArray[8][12];
					DataToSend[10] = HighScoreArray[8][13];
					DataToSend[11] = HighScoreArray[8][14];
					break;
				case 19:
					HighScoresSentYet = 1;
					SelfTestState = 3;
					SelfTestMessageMillis = CurrentProgramMillis;
					break;
				}
			}
			else
			{
				DataToSend[15] = 14;
				DataToSend[16] = 7; //High Scores Failed to Load

				Serial.println(F("High scores failed to load"));

				if (HighScoreSendState == 0)
				{
					HighScoreSendState = 1;
					HighScoreSendMillis = CurrentProgramMillis;
				}
				if (CurrentProgramMillis > HighScoreSendMillis + 3000);
				{
					HighScoresSentYet = 1;
					SelfTestState = 3;
					SelfTestMessageMillis = CurrentProgramMillis;
				}
			}
		}
		if (SelfTestState == 3) //Number of balls installed
		{
			NumBallsInTrough = HowManyBalls();
			if (NumBallsInstalled != NumBallsInTrough)
			{
				Serial.print(F("There should be "));
				Serial.print(NumBallsInstalled);
				Serial.print(F(" balls installed. There are currently only "));
				Serial.print(NumBallsInTrough);
				Serial.println(F(" balls in the trough."));
				Serial.println(" ");
				Serial.println(F("The game program will wait until the correct number of balls have been installed."));

				DataToSend[15] = 14;
				DataToSend[16] = 4;
				DataToSend[17] = NumBallsInstalled;
				SelfTestMessageMillis = CurrentProgramMillis;
			}
			else
			{
				Serial.print(F("There are the correct number of balls ("));
				Serial.print(NumBallsInstalled);
				Serial.println(F(") in the trough."));

				DataToSend[15] = 14;
				DataToSend[16] = 5;

				SelfTestState = 4;

				if (VerboseSelfTest == 0)
				{
					SelfTestState = 5;
				}
			}
		}
		if (SelfTestState == 5) //Self test pass message
		{
			DataToSend[15] = 14;
			DataToSend[16] = 6;
			Serial.println(F("Self test pass message showing"));
			SelfTestState = 6;
		}
		if (SelfTestState == 7) //Self test pass
		{
			DataToSend[15] = 0; //Move onto screen for next test
			DataToSend[16] = 0;
			SelfTestPassed = 1;
			Serial.println(F("Self test passed!"));
		}

		if (SelfTestState == 1 && CurrentProgramMillis > SelfTestMessageMillis + 3000)
		{
			SelfTestState = 2;
			HighScoreSendMillis = CurrentProgramMillis;
		}
		else if (SelfTestState == 4 && CurrentProgramMillis > SelfTestMessageMillis + 3000)
		{
			SelfTestState = 5;
			SelfTestMessageMillis = CurrentProgramMillis;
		}
		else if (SelfTestState == 6 && CurrentProgramMillis > SelfTestMessageMillis + 3000)
		{
			SelfTestState = 7;
		}
	}
}

//////////////////////////////////////////////
//                                          //
//		         Switch Matrix              //
//                                          //
//////////////////////////////////////////////

void ReadSwitches() //Reads anything that is a part of the switch matrix
{
	for (byte Drive = 0; Drive < 16; Drive++)
	{
		digitalWrite(Drive + 30, HIGH);
		for (byte Sense = 0; Sense < 8; Sense++)
		{
			LastSwitchState[Drive][Sense] = CurrentSwitchState[Drive][Sense];
			CurrentSwitchState[Drive][Sense] = digitalRead(Sense + 46);
			if (LastSwitchState[Drive][Sense] == 0 && CurrentSwitchState[Drive][Sense] == 1 && CurrentProgramMillis > LastHitTime[Drive][Sense] + TargetDebounceInterval[Drive][Sense])
			{
				HitSwitches[Drive][Sense] = 1;
				LastHitTime[Drive][Sense] = CurrentProgramMillis;
			}
			else
			{
				HitSwitches[Drive][Sense] = 0;
			}
		}
		digitalWrite(Drive + 30, LOW);
	}
	LastSwitchMatrixReadTime = CurrentProgramMillis;

	if ((TestMode == 1 || ForceTestMode == 1) && VerboseTestMode == 1) //Print switch matrix to the Serial Monitor while in test mode
	{
		Serial.println(F("Switch Matrix"));
		for (byte Drive = 0; Drive < 16; Drive++)
		{
			for (byte Sense = 0; Sense < 8; Sense++)
			{
				Serial.print(CurrentSwitchState[Drive][Sense]);
				Serial.print(F(", "));
			}
			Serial.println(" "); //go to a newline
		}
	}

	//Test function to print what switches are currently being pressed to the serial monitor
	if ((TestMode == 1 || ForceTestMode == 1) && VerboseTestMode == 1)
	{
		Serial.println(F("Switches Pressed:"));
		if (CurrentSwitchState[0][3] == 1)
		{
			Serial.println(F("\"T\"crr"));
		}
		if (CurrentSwitchState[0][4] == 1)
		{
			Serial.println(F("t\"C\"rr"));
		}
		if (CurrentSwitchState[0][5] == 1)
		{
			Serial.println(F("tc\"R\"r"));
		}
		if (CurrentSwitchState[0][6] == 1)
		{
			Serial.println(F("tcr\"R\""));
		}

		if (CurrentSwitchState[1][2] == 1)
		{
			Serial.println(F("Pop Bumper 1"));
		}

		if (CurrentSwitchState[2][0] == 1)
		{
			Serial.println(F("Left Spinner"));
		}
		if (CurrentSwitchState[2][1] == 1)
		{
			Serial.println(F("\"I\"ndians"));
		}
		if (CurrentSwitchState[2][2] == 1)
		{
			Serial.println(F("i\"N\"dians"));
		}
		if (CurrentSwitchState[2][3] == 1)
		{
			Serial.println(F("Pop Bumper 2"));
		}
		if (CurrentSwitchState[2][5] == 1)
		{
			Serial.println(F("Pop Bumper 3"));
		}
		if (CurrentSwitchState[2][6] == 1)
		{
			Serial.println(F("indi\"A\"ns"));
		}
		if (CurrentSwitchState[2][7] == 1)
		{
			Serial.println(F("Right Spinner"));
		}

		if (CurrentSwitchState[3][0] == 1)
		{
			Serial.println(F("\"R\"ail"));
		}
		if (CurrentSwitchState[3][1] == 1)
		{
			Serial.println(F("r\"A\"il"));
		}
		if (CurrentSwitchState[3][2] == 1)
		{
			Serial.println(F("ra\"I\"l"));
		}
		if (CurrentSwitchState[3][3] == 1)
		{
			Serial.println(F("in\"D\"ians"));
		}
		if (CurrentSwitchState[3][4] == 1)
		{
			Serial.println(F("Pop Bumper 4"));
		}
		if (CurrentSwitchState[3][5] == 1)
		{
			Serial.println(F("ind\"I\"ans"));
		}
		if (CurrentSwitchState[3][6] == 1)
		{
			Serial.println(F("india\"N\"s"));
		}
		if (CurrentSwitchState[3][7] == 1)
		{
			Serial.println(F("indian\"S\""));
		}

		if (CurrentSwitchState[4][0] == 1)
		{
			Serial.println(F("engi\"N\"e"));
		}
		if (CurrentSwitchState[4][1] == 1)
		{
			Serial.println(F("engin\"E\""));
		}
		if (CurrentSwitchState[4][2] == 1)
		{
			Serial.println(F("rai\"L\""));
		}
		if (CurrentSwitchState[4][5] == 1)
		{
			Serial.println(F("\"S\"team"));
		}
		if (CurrentSwitchState[4][6] == 1)
		{
			Serial.println(F("s\"T\"eam"));
		}
		if (CurrentSwitchState[4][7] == 1)
		{
			Serial.println(F("st\"E\"am"));
		}

		if (CurrentSwitchState[5][0] == 1)
		{
			Serial.println(F("eng\"I\"ne"));
		}
		if (CurrentSwitchState[5][1] == 1)
		{
			Serial.println(F("en\"G\"ine"));
		}
		if (CurrentSwitchState[5][2] == 1)
		{
			Serial.println(F("e\"N\"gine"));
		}
		if (CurrentSwitchState[5][3] == 1)
		{
			Serial.println(F("\"E\"ngine"));
		}
		if (CurrentSwitchState[5][4] == 1)
		{
			Serial.println(F("Left Slingshot"));
		}
		if (CurrentSwitchState[5][5] == 1)
		{
			Serial.println(F("Right Slingshot"));
		}
		if (CurrentSwitchState[5][6] == 1)
		{
			Serial.println(F("ste\"A\"m"));
		}
		if (CurrentSwitchState[5][7] == 1)
		{
			Serial.println(F("stea\"M\""));
		}

		if (CurrentSwitchState[6][0] == 1)
		{
			Serial.println(F("Trough 8"));
		}
		if (CurrentSwitchState[6][1] == 1)
		{
			Serial.println(F("Trough 7"));
		}
		if (CurrentSwitchState[6][2] == 1)
		{
			Serial.println(F("Trough 6"));
		}
		if (CurrentSwitchState[6][3] == 1)
		{
			Serial.println(F("Trough 5"));
		}
		if (CurrentSwitchState[6][4] == 1)
		{
			Serial.println(F("Trough 4"));
		}
		if (CurrentSwitchState[6][5] == 1)
		{
			Serial.println(F("Trough 3"));
		}
		if (CurrentSwitchState[6][6] == 1)
		{
			Serial.println(F("Trough 2"));
		}
		if (CurrentSwitchState[6][7] == 1)
		{
			Serial.println(F("Autofire"));
		}

		if (CurrentSwitchState[7][2] == 1)
		{
			Serial.println(F("OPTO Trough 1"));
		}
		if (CurrentSwitchState[7][3] == 1)
		{
			Serial.println(F("OPTO Trough Jam"));
		}
		if (CurrentSwitchState[7][4] == 1)
		{
			Serial.println(F("OPTO Indian Attack VUK"));
		}
		if (CurrentSwitchState[7][5] == 1)
		{
			Serial.println(F("OPTO Supply Tower VUK"));
		}
		if (CurrentSwitchState[7][6] == 1)
		{
			Serial.println(F("OPTO Trestle VUK"));
		}
		if (CurrentSwitchState[7][7] == 1)
		{
			Serial.println(F("OPTO Tunnel VUK"));
		}

		if (CurrentSwitchState[8][0] == 1)
		{
			Serial.println(F("CABINET Tilt Switch"));
		}
		if (CurrentSwitchState[8][1] == 1)
		{
			Serial.println(F("CABINET Left Flipper"));
		}
		if (CurrentSwitchState[8][2] == 1)
		{
			Serial.println(F("CABINET Start Button yeah boi"));
		}
		if (CurrentSwitchState[8][3] == 1)
		{
			Serial.println(F("CABINET Coin Switch $$$"));
		}
		if (CurrentSwitchState[8][4] == 1)
		{
			Serial.println(F("CABINET Test Switch"));
		}
		if (CurrentSwitchState[8][5] == 1)
		{
			Serial.println(F("CABINET Right Flipper"));
		}


		Serial.println(F(" "));
		Serial.println(F(" "));
	}
}

byte WasAnySwitchOtherThanThisOneHit(byte Row, byte Column) //Really useful for checking if I can do something like reset a drop target bank if there is the potential for the ball to get stuck behind the reset targets. 
{
	byte ReturnState = 0; //0 means no switches hit, 1 means some other switch was hit, 2 means the blacklisted switch was hit

	if (HitSwitches[Row][Column] == 1)
	{
		ReturnState = 2;
		return ReturnState;
	}

	for (byte y = 0; y < 16; y++)
	{
		for (byte x = 0; x < 8; x++)
		{
			if (HitSwitches[y][x] == 1 && y != 8) //Keep the cabinet switches from triggering this
			{
				ReturnState = 1;
				return ReturnState; //no point to keep checking
			}
		}
	}

	return ReturnState;
}

//////////////////////////////////////////////
//                                          //
//		         Coil Control               //
//                                          //
//////////////////////////////////////////////

void BreakoutBoardCoilTest()
{
	/*if (HitSwitches[8][2] == 1) //Start Button
	{
		CoilFireFlags[2][3] = 1; //Tunnel
	}*/

	if (CurrentProgramMillis > CoilTestFireMillis + 500)
	{
		CoilTestFireMillis = CurrentProgramMillis;
		WriteToCoil(CurrentCoil, 0);
		CurrentCoil++;
		if (CurrentCoil == 32)
		{
			CurrentCoil = 0;
		}
		WriteToCoil(CurrentCoil, 1);
	}
}

void FireUnfireCoils()
{
	for (byte Bank = 0; Bank < 4; Bank++)
	{
		for (byte Coil = 0; Coil < 8; Coil++)
		{
			if (CoilFireFlags[Bank][Coil] == 1 && CurrentProgramMillis < CoilFireMillis[Bank][Coil] + CoilFireCooldown[Bank][Coil]) //Acts as a bit of a coil debounce if a switch triggers because of the coil. Ex, the slingshots.
			{
				CoilFireFlags[Bank][Coil] = 0; //Doesn't refire the coil.
			}
			if (CoilFireFlags[Bank][Coil] == 1)
			{
				CoilFireFlags[Bank][Coil] = 0;
				CoilState[Bank][Coil] = 1;
				CoilFireMillis[Bank][Coil] = CurrentProgramMillis;
				WriteToCoil(8 * Bank + Coil, 1);
			}
			if (CoilState[Bank][Coil] == 1 && CurrentProgramMillis > CoilFireMillis[Bank][Coil] + CoilFireLength[Bank][Coil]) //Change CoilFireLength to an Array Later
			{
				CoilState[Bank][Coil] = 0;
				WriteToCoil(8 * Bank + Coil, 0);
			}
		}
	}
}

void WriteToCoil(byte CoilNumber, byte CoilState)
{
	if (CoilNumber == 22 && CoilState == 1)
	{
		FlippersEnabled = 1;
	}
	if (CoilNumber == 22 && CoilState == 0)
	{
		FlippersEnabled = 0;
	}
	
	if (CoilNumber > 15)
	{
		digitalWrite(CoilAddressFour, HIGH);
		CoilNumber -= 16;
	}
	else
	{
		digitalWrite(CoilAddressFour, LOW);
	}

	if (CoilNumber > 7)
	{
		digitalWrite(CoilAddressThree, HIGH);
		CoilNumber -= 8;
	}
	else
	{
		digitalWrite(CoilAddressThree, LOW);
	}

	if (CoilNumber > 3)
	{
		digitalWrite(CoilAddressTwo, HIGH);
		CoilNumber -= 4;
	}
	else
	{
		digitalWrite(CoilAddressTwo, LOW);
	}

	if (CoilNumber > 1)
	{
		digitalWrite(CoilAddressOne, HIGH);
		CoilNumber -= 2;
	}
	else
	{
		digitalWrite(CoilAddressOne, LOW);
	}

	if (CoilNumber > 0)
	{
		digitalWrite(CoilAddressZero, HIGH);
		CoilNumber -= 1;
	}
	else
	{
		digitalWrite(CoilAddressZero, LOW);
	}

	if (CoilState == 1)
	{
		digitalWrite(CoilData, HIGH);
	}
	else
	{
		digitalWrite(CoilData, LOW);
	}

	digitalWrite(CoilLatchEnable, LOW);
	digitalWrite(CoilLatchEnable, HIGH);
}

//////////////////////////////////////////////
//                                          //
//		       NeoPixel Control             //
//                                          //
//////////////////////////////////////////////
void LightTest()
{
	for (int x = 0; x < 24; x++)
	{
		lowerleds[x] = CRGB::White;
	}
	for (int x = 0; x < 26; x++)
	{
		upperleds[x] = CRGB::White;
	}
	FastLED.show();
}

void LightControl()
{
	//This function handles almost all of the NeoPixel stuff before writing to them.
	//I know that using the same blocks of code over and over again means I need to make a function, but I don't know how I'd do that for the CRGB stuff.

	UpdateRotatableLights();
	HandleEngineLights();
	HandleShootAgainLight();
	HandleIndiansLights();
	HandleTrackFrenzyLights();
	HandleTunnelLights();
	HandleTrestleLights();
	HandleRailroadSelectionLights();

	//Upper PF string
	for (int x = 0; x < 26; x++)
	{
		switch (UpperLEDState[x])
		{
		case 0:
			upperleds[x] = CRGB::Black;
			break;
		case 1:
			switch (UpperLEDColor[x])
			{
			case 0:
				upperleds[x] = CRGB::Black;
				break;
			case 1:
				upperleds[x] = CRGB::Red;
				break;
			case 2:
				upperleds[x] = CRGB::Orange;
				break;
			case 3:
				upperleds[x] = CRGB::Yellow;
				break;
			case 4:
				upperleds[x] = CRGB::Green;
				break;
			case 5:
				upperleds[x] = CRGB::Blue;
				break;
			case 6:
				upperleds[x] = CRGB::Indigo;
				break;
			case 7:
				upperleds[x] = CRGB::Violet;
				break;
			case 8:
				upperleds[x] = CRGB::White;
				break;
			default:
				upperleds[x] = CRGB::Black;
				break;
			}
			break;
		case 2: 
			//each flash sequence has the LED on and off 50/50

			//first half of each flash has light turned on
			if (CurrentProgramMillis > UpperLEDLastLit[x] + 2 * UpperLEDSpeed[x])
			{
				UpperLEDLastLit[x] = CurrentProgramMillis;
				switch (UpperLEDColor[x])
				{
				case 0:
					upperleds[x] = CRGB::Black;
					break;
				case 1:
					upperleds[x] = CRGB::Red;
					break;
				case 2:
					upperleds[x] = CRGB::Orange;
					break;
				case 3:
					upperleds[x] = CRGB::Yellow;
					break;
				case 4:
					upperleds[x] = CRGB::Green;
					break;
				case 5:
					upperleds[x] = CRGB::Blue;
					break;
				case 6:
					upperleds[x] = CRGB::Indigo;
					break;
				case 7:
					upperleds[x] = CRGB::Violet;
					break;
				case 8:
					upperleds[x] = CRGB::White;
					break;
				default:
					upperleds[x] = CRGB::Black;
					break;
				}
			}
			else if (CurrentProgramMillis > UpperLEDLastLit[x] + UpperLEDSpeed[x]) //second half of each flash has light turned off.
			{
				upperleds[x] = CRGB::Black;
			}
			break;
		default:
			break;
		}
	}

	//Lower PF string
	for (int x = 0; x < 24; x++)
	{
		switch (LowerLEDState[x])
		{
		case 0:
			lowerleds[x] = CRGB::Black;
			break;
		case 1:
			switch (LowerLEDColor[x])
			{
			case 0:
				lowerleds[x] = CRGB::Black;
				break;
			case 1:
				lowerleds[x] = CRGB::Red;
				break;
			case 2:
				lowerleds[x] = CRGB::Orange;
				break;
			case 3:
				lowerleds[x] = CRGB::Yellow;
				break;
			case 4:
				lowerleds[x] = CRGB::Green;
				break;
			case 5:
				lowerleds[x] = CRGB::Blue;
				break;
			case 6:
				lowerleds[x] = CRGB::Indigo;
				break;
			case 7:
				lowerleds[x] = CRGB::Violet;
				break;
			case 8:
				lowerleds[x] = CRGB::White;
				break;
			default:
				lowerleds[x] = CRGB::Black;
				break;
			}
			break;
		case 2:
			//each flash sequence has the LED on and off 50/50

			//first half of each flash has light turned on
			if (CurrentProgramMillis > LowerLEDLastLit[x] + 2 * LowerLEDSpeed[x])
			{
				LowerLEDLastLit[x] = CurrentProgramMillis;
				switch (LowerLEDColor[x])
				{
				case 0:
					lowerleds[x] = CRGB::Black;
					break;
				case 1:
					lowerleds[x] = CRGB::Red;
					break;
				case 2:
					lowerleds[x] = CRGB::Orange;
					break;
				case 3:
					lowerleds[x] = CRGB::Yellow;
					break;
				case 4:
					lowerleds[x] = CRGB::Green;
					break;
				case 5:
					lowerleds[x] = CRGB::Blue;
					break;
				case 6:
					lowerleds[x] = CRGB::Indigo;
					break;
				case 7:
					lowerleds[x] = CRGB::Violet;
					break;
				case 8:
					lowerleds[x] = CRGB::White;
					break;
				default:
					lowerleds[x] = CRGB::Black;
					break;
				}
			}
			else if (CurrentProgramMillis > LowerLEDLastLit[x] + LowerLEDSpeed[x]) //second half of each flash has light turned off.
			{
				lowerleds[x] = CRGB::Black;
			}
			break;
		default:
			break;
		}
	}

	FastLED.show();
}

void UpdateRotatableLights()
{
	if (TCRRCompleted == 0)
	{
		for (int x = 0; x < 4; x++)
		{
			if (TCRRTriggered[x] == 1)
			{
				UpperLEDColor[x] = 8;
				UpperLEDState[x] = 1;
			}
			else
			{
				UpperLEDState[x] = 0;
			}
		}
	}
	
	if (STEAMCompleted == 0)
	{
		for (int x = 0; x < 5; x++)
		{
			if (STEAMTriggered[x] == 1)
			{
				LowerLEDColor[x + 6] = 8;
				LowerLEDState[x + 6] = 1;
			}
			else
			{
				LowerLEDState[x + 6] = 0;
			}
		}
	}

	if (TCRRCompleted == 1)
	{
		UpperLEDColor[0] = 8;
		UpperLEDColor[1] = 8;
		UpperLEDColor[2] = 8;
		UpperLEDColor[3] = 8;
		
		UpperLEDState[0] = 2;
		UpperLEDState[1] = 2;
		UpperLEDState[2] = 2;
		UpperLEDState[3] = 2;

		UpperLEDSpeed[0] = 125;
		UpperLEDSpeed[1] = 125;
		UpperLEDSpeed[2] = 125;
		UpperLEDSpeed[3] = 125;

		UpperLEDLastLit[0] = CurrentProgramMillis; //This is not necessary to make the flash function work; it ensures that all the lights flash in unison.
		UpperLEDLastLit[1] = CurrentProgramMillis; //This was done to fix a rare bug.
		UpperLEDLastLit[2] = CurrentProgramMillis;
		UpperLEDLastLit[3] = CurrentProgramMillis;

		TCRRCompletedMillis = CurrentProgramMillis;

		TCRRCompleted = 2;
	}
	if (TCRRCompleted == 2 && CurrentProgramMillis > TCRRCompletedMillis + 2000)
	{
		TCRRCompleted = 0;
		UpperLEDState[0] = 0;
		UpperLEDState[1] = 0;
		UpperLEDState[2] = 0;
		UpperLEDState[3] = 0;
	}

	if (STEAMCompleted == 1)
	{
		LowerLEDColor[6] = 8;
		LowerLEDColor[7] = 8;
		LowerLEDColor[8] = 8;
		LowerLEDColor[9] = 8;
		LowerLEDColor[10] = 8;
		
		LowerLEDState[6] = 2;
		LowerLEDState[7] = 2;
		LowerLEDState[8] = 2;
		LowerLEDState[9] = 2;
		LowerLEDState[10] = 2;

		LowerLEDSpeed[6] = 125;
		LowerLEDSpeed[7] = 125;
		LowerLEDSpeed[8] = 125;
		LowerLEDSpeed[9] = 125;
		LowerLEDSpeed[10] = 125;

		LowerLEDLastLit[6] = CurrentProgramMillis;
		LowerLEDLastLit[7] = CurrentProgramMillis;
		LowerLEDLastLit[8] = CurrentProgramMillis;
		LowerLEDLastLit[9] = CurrentProgramMillis;
		LowerLEDLastLit[10] = CurrentProgramMillis;

		STEAMCompletedMillis = CurrentProgramMillis;

		STEAMCompleted = 2;
	}
	if (STEAMCompleted == 2 && CurrentProgramMillis > STEAMCompletedMillis + 2000)
	{
		LowerLEDState[6] = 0;
		LowerLEDState[7] = 0;
		LowerLEDState[8] = 0;
		LowerLEDState[9] = 0;
		LowerLEDState[10] = 0;

		STEAMCompleted = 0;
	}
}

void HandleEngineLights()
{
	if (ENGINETriggered[0] == 1)
	{
		LowerLEDColor[5] = 8;
		LowerLEDState[5] = 1;
	}

	if (ENGINETriggered[1] == 1)
	{
		LowerLEDColor[4] = 8;
		LowerLEDState[4] = 1;
	}

	if (ENGINETriggered[2] == 1)
	{
		LowerLEDColor[3] = 8;
		LowerLEDState[3] = 1;
	}

	if (ENGINETriggered[3] == 1)
	{
		LowerLEDColor[2] = 8;
		LowerLEDState[2] = 1;
	}

	if (ENGINETriggered[4] == 1)
	{
		LowerLEDColor[1] = 8;
		LowerLEDState[1] = 1;
	}

	if (ENGINETriggered[5] == 1)
	{
		LowerLEDColor[0] = 8;
		LowerLEDState[0] = 1;
	}
	
	if (ENGINECompleted == 1)
	{
		ENGINECompleted = 2;

		LowerLEDColor[0] = 8;
		LowerLEDColor[1] = 8;
		LowerLEDColor[2] = 8;
		LowerLEDColor[3] = 8;
		LowerLEDColor[4] = 8;
		LowerLEDColor[5] = 8;
		
		LowerLEDState[0] = 2;
		LowerLEDState[1] = 2;
		LowerLEDState[2] = 2;
		LowerLEDState[3] = 2;
		LowerLEDState[4] = 2;
		LowerLEDState[5] = 2;

		LowerLEDSpeed[0] = 125;
		LowerLEDSpeed[1] = 125;
		LowerLEDSpeed[2] = 125;
		LowerLEDSpeed[3] = 125;
		LowerLEDSpeed[4] = 125;
		LowerLEDSpeed[5] = 125;

		LowerLEDLastLit[0] = CurrentProgramMillis;
		LowerLEDLastLit[1] = CurrentProgramMillis;
		LowerLEDLastLit[2] = CurrentProgramMillis;
		LowerLEDLastLit[3] = CurrentProgramMillis;
		LowerLEDLastLit[4] = CurrentProgramMillis;
		LowerLEDLastLit[5] = CurrentProgramMillis;
	}

	if (ENGINECompleted == 2 && CurrentProgramMillis > ENGINECompletedMillis + 2000)
	{
		ENGINECompleted = 0;

		LowerLEDState[0] = 0;
		LowerLEDState[1] = 0;
		LowerLEDState[2] = 0;
		LowerLEDState[3] = 0;
		LowerLEDState[4] = 0;
		LowerLEDState[5] = 0;
	}

	if (NumberOfEnginesAcquired > 0)
	{
		LowerLEDColor[21] = 8;
		LowerLEDState[21] = 1;
	}
	else if (NumberOfEnginesAcquired == 0)
	{
		LowerLEDState[21] = 0;
	}
}

void HandleShootAgainLight()
{
	if (ShootAgainActive == 0)
	{
		return;
	}

	if (MultiballModeActive == 1)
	{
		if (INDIANSModeActive == 1)
		{
			if (CurrentProgramMillis > ShootAgainMillis + INDIANSMultiballGracePeriod)
			{
				LowerLEDState[23] = 0;

				ShootAgainActive = 0;
			}
			else if (CurrentProgramMillis > ShootAgainMillis + INDIANSMultiballGracePeriod - 2000)
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 2;
				LowerLEDSpeed[23] = 125;
			}
			else if (CurrentProgramMillis > ShootAgainMillis + INDIANSMultiballGracePeriod - 5000)
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 2;
				LowerLEDSpeed[23] = 350;
			}
			else
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 1;
			}
		}
		else if (TrackFrenzyModeActive == 1)
		{
			if (CurrentProgramMillis > ShootAgainMillis + TrackFrenzyMultiballGracePeriod)
			{
				LowerLEDState[23] = 0;

				ShootAgainActive = 0;
			}
			else if (CurrentProgramMillis > ShootAgainMillis + TrackFrenzyMultiballGracePeriod - 2000)
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 2;
				LowerLEDSpeed[23] = 125;
			}
			else if (CurrentProgramMillis > ShootAgainMillis + TrackFrenzyMultiballGracePeriod - 5000)
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 2;
				LowerLEDSpeed[23] = 350;
			}
			else
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 1;
			}
		}
		else if (RailroadCompletedMultiballModeActive == 1)
		{
			if (CurrentProgramMillis > ShootAgainMillis + RailroadCompletedMultiballGracePeriod)
			{
				LowerLEDState[23] = 0;

				ShootAgainActive = 0;
			}
			else if (CurrentProgramMillis > ShootAgainMillis + RailroadCompletedMultiballGracePeriod - 2000)
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 2;
				LowerLEDSpeed[23] = 125;
			}
			else if (CurrentProgramMillis > ShootAgainMillis + RailroadCompletedMultiballGracePeriod - 5000)
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 2;
				LowerLEDSpeed[23] = 350;
			}
			else
			{
				LowerLEDColor[23] = 8;
				LowerLEDState[23] = 1;
			}
		}
	}
	else
	{
		if (CurrentProgramMillis > ShootAgainMillis + ShootAgainGracePeriod)
		{
			LowerLEDState[23] = 0;

			ShootAgainActive = 0;
		}
		else if (CurrentProgramMillis > ShootAgainMillis + ShootAgainGracePeriod - 2000)
		{
			LowerLEDColor[23] = 8;
			LowerLEDState[23] = 2;
			LowerLEDSpeed[23] = 125;
		}
		else if (CurrentProgramMillis > ShootAgainMillis + ShootAgainGracePeriod - 5000)
		{
			LowerLEDColor[23] = 8;
			LowerLEDState[23] = 2;
			LowerLEDSpeed[23] = 350;
		}
		else
		{
			LowerLEDColor[23] = 8;
			LowerLEDState[23] = 1;
		}
	}
}

void HandleIndiansLights()
{
	if (INDIANSModeStage == 1) //Get 'em started flashing
	{
		UpperLEDState[7] = 2;
		UpperLEDState[8] = 2;
		UpperLEDState[12] = 2;
		UpperLEDState[13] = 2;
		UpperLEDState[15] = 2;
		UpperLEDState[16] = 2;
		UpperLEDState[18] = 2;

		UpperLEDColor[7] = 3;
		UpperLEDColor[8] = 3;
		UpperLEDColor[12] = 3;
		UpperLEDColor[13] = 3;
		UpperLEDColor[15] = 3;
		UpperLEDColor[16] = 3;
		UpperLEDColor[18] = 3;

		UpperLEDSpeed[7] = 250;
		UpperLEDSpeed[8] = 250;
		UpperLEDSpeed[12] = 250;
		UpperLEDSpeed[13] = 250;
		UpperLEDSpeed[15] = 250;
		UpperLEDSpeed[16] = 250;
		UpperLEDSpeed[18] = 250;
	}

	if (INDIANSTriggered[0] == 1)
	{
		UpperLEDColor[18] = 8;
		UpperLEDState[18] = 1;
	}
	else if (INDIANSModeActive == 1 && INDIANSCompleted != 2 && INDIANSTriggered[0] == 0)
	{
		UpperLEDColor[18] = 3;
		UpperLEDState[18] = 2;
		UpperLEDSpeed[18] = 250;
	}
	else if(INDIANSModeActive == 0)
	{
		UpperLEDState[18] = 0;
	}

	if (INDIANSTriggered[1] == 1)
	{
		UpperLEDColor[16] = 8;
		UpperLEDState[16] = 1;
	}
	else if (INDIANSModeActive == 1 && INDIANSCompleted != 2 && INDIANSTriggered[1] == 0)
	{
		UpperLEDColor[16] = 3;
		UpperLEDState[16] = 2;
		UpperLEDSpeed[16] = 250;
	}
	else if (INDIANSModeActive == 0)
	{
		UpperLEDState[16] = 0;
	}

	if (INDIANSTriggered[2] == 1)
	{
		UpperLEDColor[15] = 8;
		UpperLEDState[15] = 1;
	}
	else if (INDIANSModeActive == 1 && INDIANSCompleted != 2 && INDIANSTriggered[2] == 0)
	{
		UpperLEDColor[15] = 3;
		UpperLEDState[15] = 2;
		UpperLEDSpeed[15] = 250;
	}
	else if (INDIANSModeActive == 0)
	{
		UpperLEDState[15] = 0;
	}

	if (INDIANSTriggered[3] == 1)
	{
		UpperLEDColor[13] = 8;
		UpperLEDState[13] = 1;
	}
	else if (INDIANSModeActive == 1 && INDIANSCompleted != 2 && INDIANSTriggered[3] == 0)
	{
		UpperLEDColor[13] = 3;
		UpperLEDState[13] = 2;
		UpperLEDSpeed[13] = 250;
	}
	else if (INDIANSModeActive == 0)
	{
		UpperLEDState[13] = 0;
	}

	if (INDIANSTriggered[4] == 1)
	{
		UpperLEDColor[12] = 8;
		UpperLEDState[12] = 1;
	}
	else if (INDIANSModeActive == 1 && INDIANSCompleted != 2 && INDIANSTriggered[4] == 0)
	{
		UpperLEDColor[12] = 3;
		UpperLEDState[12] = 2;
		UpperLEDSpeed[12] = 250;
	}
	else if (INDIANSModeActive == 0)
	{
		UpperLEDState[12] = 0;
	}

	if (INDIANSTriggered[5] == 1)
	{
		UpperLEDColor[7] = 8;
		UpperLEDState[7] = 1;
	}
	else if (INDIANSModeActive == 1 && INDIANSCompleted != 2 && INDIANSTriggered[5] == 0)
	{
		UpperLEDColor[7] = 3;
		UpperLEDState[7] = 2;
		UpperLEDSpeed[7] = 250;
	}
	else if (INDIANSModeActive == 0)
	{
		UpperLEDState[7] = 0;
	}

	if (INDIANSTriggered[6] == 1)
	{
		UpperLEDColor[8] = 8;
		UpperLEDState[8] = 1;
	}
	else if (INDIANSModeActive == 1 && INDIANSCompleted != 2 && INDIANSTriggered[6] == 0)
	{
		UpperLEDColor[8] = 3;
		UpperLEDState[8] = 2;
		UpperLEDSpeed[8] = 250;
	}
	else if (INDIANSModeActive == 0)
	{
		UpperLEDState[8] = 0;
	}

	if (INDIANSCompleted == 1)
	{
		UpperLEDColor[7] = 8;
		UpperLEDColor[8] = 8;
		UpperLEDColor[12] = 8;
		UpperLEDColor[13] = 8;
		UpperLEDColor[15] = 8;
		UpperLEDColor[16] = 8;
		UpperLEDColor[18] = 8;

		UpperLEDState[7] = 2;
		UpperLEDState[8] = 2;
		UpperLEDState[12] = 2;
		UpperLEDState[13] = 2;
		UpperLEDState[15] = 2;
		UpperLEDState[16] = 2;
		UpperLEDState[18] = 2;

		UpperLEDSpeed[7] = 125;
		UpperLEDSpeed[8] = 125;
		UpperLEDSpeed[12] = 125;
		UpperLEDSpeed[13] = 125;
		UpperLEDSpeed[15] = 125;
		UpperLEDSpeed[16] = 125;
		UpperLEDSpeed[18] = 125;

		UpperLEDLastLit[7] = CurrentProgramMillis;
		UpperLEDLastLit[8] = CurrentProgramMillis;
		UpperLEDLastLit[12] = CurrentProgramMillis;
		UpperLEDLastLit[13] = CurrentProgramMillis;
		UpperLEDLastLit[15] = CurrentProgramMillis;
		UpperLEDLastLit[16] = CurrentProgramMillis;
		UpperLEDLastLit[18] = CurrentProgramMillis;

		INDIANSCompleted = 2;
	}
	if (INDIANSCompleted == 2 && CurrentProgramMillis > INDIANSCompletedMillis + 2000)
	{
		UpperLEDState[7] = 0;
		UpperLEDState[8] = 0;
		UpperLEDState[12] = 0;
		UpperLEDState[13] = 0;
		UpperLEDState[15] = 0;
		UpperLEDState[16] = 0;
		UpperLEDState[18] = 0;

		INDIANSCompleted = 0;
		if (INDIANSModeActive == 1) //Return the INDIANS lights to flashing yellow
		{
			UpperLEDState[7] = 2;
			UpperLEDState[8] = 2;
			UpperLEDState[12] = 2;
			UpperLEDState[13] = 2;
			UpperLEDState[15] = 2;
			UpperLEDState[16] = 2;
			UpperLEDState[18] = 2;

			UpperLEDColor[7] = 3;
			UpperLEDColor[8] = 3;
			UpperLEDColor[12] = 3;
			UpperLEDColor[13] = 3;
			UpperLEDColor[15] = 3;
			UpperLEDColor[16] = 3;
			UpperLEDColor[18] = 3;

			UpperLEDSpeed[7] = 250;
			UpperLEDSpeed[8] = 250;
			UpperLEDSpeed[12] = 250;
			UpperLEDSpeed[13] = 250;
			UpperLEDSpeed[15] = 250;
			UpperLEDSpeed[16] = 250;
			UpperLEDSpeed[18] = 250;
		}
	}
	if (NumberOfIndianSquabbles > 0 || INDIANSGoalCompleted == 1)
	{
		LowerLEDColor[22] = 8;
		LowerLEDState[22] = 1;
	}
	else if (INDIANSModeActive == 1)
	{
		LowerLEDColor[22] = 8;
		LowerLEDState[22] = 2;
		LowerLEDSpeed[22] = 250;
	}
	else
	{
		LowerLEDState[22] = 0;
	}

	if (RailroadCompletedMultiballModeActive == 1)
	{
		UpperLEDColor[17] = 3;
		UpperLEDState[17] = 2;
		UpperLEDSpeed[17] = 250;
	}
	else if (RAILCompleted == 0)
	{
		UpperLEDState[17] = 0;
	}
}

void HandleTrackFrenzyLights()
{
	if (TrackFrenzyModeActive == 1 || RailroadCompletedMultiballModeActive == 1)
	{
		if (CurrentProgramMillis > TrackFrenzyLightMillis + 150)
		{
			if (TrackFrenzyLightState == 0)
			{
				TrackFrenzyLightState = 1;
				TrackFrenzyLightMillis = CurrentProgramMillis;

				
				UpperLEDState[4] = 0;
				UpperLEDState[21] = 0;
				
				UpperLEDColor[6] = 3;
				UpperLEDState[6] = 1;

				UpperLEDColor[19] = 3;
				UpperLEDState[19] = 1;
			}
			else if (TrackFrenzyLightState == 1)
			{
				TrackFrenzyLightState = 2;
				TrackFrenzyLightMillis = CurrentProgramMillis;

				UpperLEDState[6] = 0;
				UpperLEDState[19] = 0;

				UpperLEDColor[5] = 3;
				UpperLEDState[5] = 1;

				UpperLEDColor[20] = 3;
				UpperLEDState[20] = 1;
			}
			else if (TrackFrenzyLightState == 2)
			{
				TrackFrenzyLightState = 0;
				TrackFrenzyLightMillis = CurrentProgramMillis;

				UpperLEDState[5] = 0;
				UpperLEDState[20] = 0;

				UpperLEDColor[4] = 3;
				UpperLEDState[4] = 1;

				UpperLEDColor[21] = 3;
				UpperLEDState[21] = 1;
			}
		}
	}
	else if(DiverterOpen == 0)
	{
		UpperLEDState[4] = 0;
		UpperLEDState[5] = 0;
		UpperLEDState[6] = 0;

		UpperLEDState[19] = 0;
		UpperLEDState[20] = 0;
		UpperLEDState[21] = 0;

		TrackFrenzyLightState = 0;
	}
	else
	{
		UpperLEDState[19] = 0;
		UpperLEDState[20] = 0;
		UpperLEDState[21] = 0;

		TrackFrenzyLightState = 0;
	}

	if (TrackFrenziesCompleted != 0 || MilesOfTrackBuiltDuringFrenzy > 99)
	{
		LowerLEDColor[20] = 8;
		LowerLEDState[20] = 1;
	}
	else if (TrackFrenzyModeActive == 1)
	{
		LowerLEDColor[20] = 8;
		LowerLEDState[20] = 2;
		LowerLEDSpeed[20] = 250;
	}
	else
	{
		LowerLEDState[20] = 0;
	}

	if (MutuallyExclusiveModeActive == 0 && TrackFrenzyHappenedYet == 0 && GameActive == 1)
	{
		UpperLEDColor[14] = 1;
		UpperLEDState[14] = 2;
		UpperLEDSpeed[14] = 200;
	}
	else if ((RailroadCompletedMultiballModeActive == 1 || TrackFrenzyModeActive == 1) && GameActive == 1)
	{
		UpperLEDColor[14] = 3;
		UpperLEDState[14] = 2;
		UpperLEDSpeed[14] = 250;
	}
	else if (MutuallyExclusiveModeActive == 0 && RailroadCompleted == 1 && GameActive == 1)
	{
		UpperLEDColor[14] = 1;
		UpperLEDState[14] = 2;
		UpperLEDSpeed[14] = 100; //Make it flash frantically!
	}
	else if (MutuallyExclusiveModeActive == 0 && PlayerOneScore >= LastExtraBallScore + ExtraBallAwardedAtPointScore)
	{
		UpperLEDColor[14] = 3;
		UpperLEDState[14] = 2;
		UpperLEDSpeed[14] = 250;
	}
	else
	{
		UpperLEDState[14] = 0;
	}
}

void HandleTunnelLights()
{
	if (NitroglycerineHit[0] == 1)
	{
		UpperLEDColor[11] = 8;
		UpperLEDState[11] = 1;
	}
	else
	{
		UpperLEDState[11] = 0;
	}

	if (NitroglycerineHit[1] == 1)
	{
		UpperLEDColor[9] = 8;
		UpperLEDState[9] = 1;
	}
	else
	{
		UpperLEDState[9] = 0;
	}

	if (PartialTunnelsCompleted == 1 || RailroadCompletedMultiballModeActive == 1)
	{
		UpperLEDColor[10] = 3;
		UpperLEDState[10] = 2;
		UpperLEDSpeed[10] = 250;
	}
	else
	{
		UpperLEDState[10] = 0;
	}

	if (NumberOfTunnelsBlasted != 0)
	{
		LowerLEDColor[18] = 8;
		LowerLEDState[18] = 1;
	}
	else if(PartialTunnelsCompleted == 1)
	{
		LowerLEDColor[18] = 8;
		LowerLEDState[18] = 2;
		LowerLEDSpeed[18] = 250;
	}
	else
	{
		LowerLEDState[18] = 0;
	}
}

void HandleTrestleLights()
{
	if (MutuallyExclusiveModeActive == 0 && DiverterOpen == 1)
	{
		if (CurrentProgramMillis > TrestleLightMillis + 150)
		{
			if (TrestleLightState == 0)
			{
				TrestleLightState = 1;
				TrestleLightMillis = CurrentProgramMillis;


				UpperLEDState[4] = 0;

				UpperLEDColor[6] = 3;
				UpperLEDState[6] = 1;
			}
			else if (TrestleLightState == 1)
			{
				TrestleLightState = 2;
				TrestleLightMillis = CurrentProgramMillis;

				UpperLEDState[6] = 0;

				UpperLEDColor[5] = 3;
				UpperLEDState[5] = 1;
			}
			else if (TrestleLightState == 2)
			{
				TrestleLightState = 0;
				TrestleLightMillis = CurrentProgramMillis;

				UpperLEDState[5] = 0;

				UpperLEDColor[4] = 3;
				UpperLEDState[4] = 1;
			}
		}
	}
	else if(MutuallyExclusiveModeActive == 0)
	{
		UpperLEDState[4] = 0;
		UpperLEDState[5] = 0;
		UpperLEDState[6] = 0;

		TrestleLightState = 0;
	}

	if (NumberOfTrestlesBuilt != 0)
	{
		LowerLEDColor[19] = 8;
		LowerLEDState[19] = 1;
	}
	else if (PartialTrestlesCompleted == 1)
	{
		LowerLEDColor[19] = 8;
		LowerLEDState[19] = 2;
		LowerLEDSpeed[19] = 250;
	}
	else
	{
		LowerLEDState[19] = 0;
	}
}

void HandleRailroadSelectionLights()
{
	if (GameActive == 1 || RailroadSelectModeActive == 1)
	{
		if (CentralPacificCompleted == 1)
		{
			LowerLEDColor[13] = 8;
			LowerLEDColor[14] = 8;

			LowerLEDState[13] = 1;
			LowerLEDState[14] = 1;
		}
		else if (ChosenRailroad == 1)
		{
			LowerLEDColor[13] = 8;
			LowerLEDColor[14] = 8;

			LowerLEDState[13] = 2;
			LowerLEDState[14] = 2;

			LowerLEDSpeed[13] = 350;
			LowerLEDSpeed[14] = 350;
		}
		else
		{
			LowerLEDState[13] = 0;
			LowerLEDState[14] = 0;
		}

		if (UnionPacificCompleted == 1)
		{
			LowerLEDColor[11] = 8;
			LowerLEDColor[12] = 8;

			LowerLEDState[11] = 1;
			LowerLEDState[12] = 1;
		}
		else if (ChosenRailroad == 2)
		{
			LowerLEDColor[11] = 8;
			LowerLEDColor[12] = 8;

			LowerLEDState[11] = 2;
			LowerLEDState[12] = 2;

			LowerLEDSpeed[11] = 350;
			LowerLEDSpeed[12] = 350;
		}
		else
		{
			LowerLEDState[11] = 0;
			LowerLEDState[12] = 0;
		}
	}
}

///////////////////////////////////////////////////////////
//                                                       //
//		 Communication & Data Preparing Functions        //
//                                                       //
///////////////////////////////////////////////////////////

void SendAndReceiveData()
{
	UpdateDataArray(); //This function means I don't have to put a bunch of stuff in the main loop. Recalculates everything, still might be putting in the same data.
	CheckForNewData();

	if (NewDataPresent == 0) //Don't tie up the program by sending data if there is nothing new to send.
	{
		return;
	}

	if (Serial3.availableForWrite() < 64) //Make sure it doesn't write when the DMD still hasn't processed the previous data.
	{
		return;
	}

	//All abort conditions passed, send the data!
	for (int x = 0; x < 21; x++)
	{
		Serial3.write(DataToSend[x]);
		LastDataThatWasSent[x] = DataToSend[x];
	}

	NewDataPresent = 0;
}

void CheckForNewData()
{
	for (int x = 0; x < 21; x++)
	{
		if (DataToSend[x] != LastDataThatWasSent[x])
		{
			NewDataPresent = 1;
			return;
		}
	}
}

void UpdateDataArray()
{
	//Clear "old" data
	if (HighScoresSentYet == 1)
	{
		DataToSend[2] = 0;
		DataToSend[3] = 0;
		DataToSend[4] = 0;
		DataToSend[5] = 0;
		DataToSend[6] = 0;
		DataToSend[7] = 0;
		DataToSend[8] = 0;
		DataToSend[9] = 0;
		DataToSend[10] = 0;
		DataToSend[11] = 0;
	}
	DataToSend[12] = 0;
	DataToSend[13] = 0;
	
	DataToSend[0] = 201; //This is mandatory! If this number is not sent, comms will not work.

	if (GameActive == 0 && (GameStartState == 0 || RailroadSelectModeActive == 1))
	{
		DataToSend[1] = 0;
	}
	else if (GameStartState != 0 || GameActive == 1)
	{
		DataToSend[1] = 1;
	}
	

	//Break down the player 1 score
	if (HighScoresSentYet == 1)
	{
		ScoreBreakdownVariable = PlayerOneScore;
		while (ScoreBreakdownVariable >= 1000000000)
		{
			ScoreBreakdownVariable -= 1000000000;
			DataToSend[2]++;
		}
		while (ScoreBreakdownVariable >= 100000000)
		{
			ScoreBreakdownVariable -= 100000000;
			DataToSend[3]++;
		}
		while (ScoreBreakdownVariable >= 10000000)
		{
			ScoreBreakdownVariable -= 10000000;
			DataToSend[4]++;
		}
		while (ScoreBreakdownVariable >= 1000000)
		{
			ScoreBreakdownVariable -= 1000000;
			DataToSend[5]++;
		}
		while (ScoreBreakdownVariable >= 100000)
		{
			ScoreBreakdownVariable -= 100000;
			DataToSend[6]++;
		}
		while (ScoreBreakdownVariable >= 10000)
		{
			ScoreBreakdownVariable -= 10000;
			DataToSend[7]++;
		}
		while (ScoreBreakdownVariable >= 1000)
		{
			ScoreBreakdownVariable -= 1000;
			DataToSend[8]++;
		}
		while (ScoreBreakdownVariable >= 100)
		{
			ScoreBreakdownVariable -= 100;
			DataToSend[9]++;
		}
		while (ScoreBreakdownVariable >= 10)
		{
			ScoreBreakdownVariable -= 10;
			DataToSend[10]++;
		}
		while (ScoreBreakdownVariable >= 1)
		{
			ScoreBreakdownVariable -= 1;
			DataToSend[11]++;
		}
	}

	//Load the correct number of balls and credits into the array
	if (CurrentBallNum < 1)
	{
		DataToSend[14] = 1; //Sometimes the game may say there are 0 balls
	}
	else
	{
		DataToSend[14] = CurrentBallNum;
	}

	MathNumberOfCredits = NumCoinsInserted;
	TransmissionNumberOfCredits = 0;
	if (PricePerGame != 0)
	{
		while (MathNumberOfCredits >= PricePerGame)
		{
			MathNumberOfCredits -= PricePerGame;
			TransmissionNumberOfCredits++;
		}
		MathNumberOfCredits = TransmissionNumberOfCredits;
		while (MathNumberOfCredits >= 10)
		{
			MathNumberOfCredits -= 10;
			DataToSend[12]++;
		}
		while (MathNumberOfCredits >= 1)
		{
			MathNumberOfCredits -= 1;
			DataToSend[13]++;
		}
	}

	if (GameActive == 0 && RailroadSelectModeActive == 1)
	{
		DataToSend[15] = 1;
		DataToSend[17] = ChosenRailroad;

		//Figure out, send the time left to choose
		MathVariable = ImpatientRailroadSelectMillis + 15000 - CurrentProgramMillis;
		DataToSend[18] = 0;
		while (MathVariable >= 1000)
		{
			MathVariable -= 1000;
			DataToSend[18]++; //Time left to choose
		}
	}
	else if (GameActive == 0 && HighScoresSentYet == 1 && SelfTestPassed == 1)
	{
		DataToSend[17] = PricePerGame;
		DataToSend[15] = 0;
	}

	if (TrackFrenzyModeActive == 1)
	{
		DataToSend[17] = TrackFrenzyMilesGoal;
		
		MathVariable = MilesOfTrackBuiltDuringFrenzy;
		MathVariableTwo = 0;
		while (MathVariable > 255)
		{
			MathVariable -= 256;
			MathVariableTwo++;
		}
		DataToSend[18] = MathVariableTwo;
		DataToSend[19] = MathVariable;
	}

	if (MutuallyExclusiveModeActive == 1 || DiverterOpen == 1 || VUKTriggered[2] == 1 || BallSavedState == 1 || ExtraBallMessageState == 1 || EndOfBallBonusStage != 0)
	{
		TrackProgressMessageState = 0;
	}
	if (MutuallyExclusiveModeActive == 0 && TrackProgressMessageState == 1)
	{
		DataToSend[15] = 8;

		MathVariable = MilesOfTrackLaid;
		MathVariableTwo = 0;
		while (MathVariable > 255)
		{
			MathVariableTwo++;
			MathVariable -= 256;
		}

		if (MilesOfTrackLaid > 65535)
		{
			DataToSend[17] = 255;
			DataToSend[18] = 255;
		}
		else
		{
			DataToSend[17] = MathVariableTwo;
			DataToSend[18] = MathVariable;
		}

		if (ChosenRailroad == 1) //CPRR
		{
			MathVariable = CPRRMilesOfTrack;
			MathVariableTwo = 0;
			while (MathVariable > 255)
			{
				MathVariableTwo++;
				MathVariable -= 256;
			}
		}
		else if (ChosenRailroad == 2) //UPRR
		{
			MathVariable = UPRRMilesOfTrack;
			MathVariableTwo = 0;
			while (MathVariable > 255)
			{
				MathVariableTwo++;
				MathVariable -= 256;
			}
		}

		DataToSend[19] = MathVariableTwo;
		DataToSend[20] = MathVariable;
	}
	if (TrackProgressMessageState == 1 && CurrentProgramMillis > TrackProgressMessageMillis + 2000)
	{
		if (MutuallyExclusiveModeActive == 0)
		{
			DataToSend[15] = 0;
		}

		TrackProgressMessageState = 0;
	}

	if (INDIANSModeMessageState == 4 && CurrentProgramMillis > INDIANSModeMessageMillis + 3000 && INDIANSModeActive == 1)
	{
		INDIANSModeMessageState = 3;

		DataToSend[15] = 9;
		DataToSend[16] = 3;
	}
	if (INDIANSModeMessageState == 3 && INDIANSModeActive == 1)
	{
		DataToSend[15] = 9;
		DataToSend[16] = 3;
	}
	if (INDIANSModeMessageState == 5 && CurrentProgramMillis > INDIANSModeMessageMillis + 3000) //INDIANS Defeated
	{
		INDIANSModeMessageState = 0;

		DataToSend[15] = 0;
	}
	if (INDIANSModeMessageState == 6 && CurrentProgramMillis > INDIANSModeMessageMillis + 3000) //RAIL Crew Killed
	{
		INDIANSModeMessageState = 0;

		DataToSend[15] = 0;
	}
	if (INDIANSModeActive == 1)
	{
		DataToSend[17] = 0;
		DataToSend[17] += INDIANSTriggered[0] * 64; //I
		DataToSend[17] += INDIANSTriggered[1] * 32; //N
		DataToSend[17] += INDIANSTriggered[2] * 16; //D
		DataToSend[17] += INDIANSTriggered[3] * 8;  //I
		DataToSend[17] += INDIANSTriggered[4] * 4;  //A
		DataToSend[17] += INDIANSTriggered[5] * 2;  //N
		DataToSend[17] += INDIANSTriggered[6] * 1;  //S
	}

	if (MutuallyExclusiveModeActive == 0 && BallSavedState == 1 && CurrentProgramMillis > BallSavedMillis + 1500)
	{
		DataToSend[15] = 0;
		BallSavedState = 0;
	}

	if (MutuallyExclusiveModeActive == 0 && CurrentProgramMillis < ExtraBallMessageMillis + 5000 && ExtraBallMessageState == 1)
	{
		DataToSend[15] = 11;
	}
	if (ExtraBallMessageState == 1 && CurrentProgramMillis > ExtraBallMessageMillis + 5000)
	{
		ExtraBallMessageState = 2;
		
		DataToSend[15] = 0;
	}
}